Reference
API reference
Everything exported from src/index.js and build/index.js. The types are in build/*.d.ts.
import {
MagicSystem, PRESETS, getPresetParams, DEFAULT_PARAMS, QUALITY_LEVELS,
EFFECT_TYPES, MagicEffect, PortalEffect, GenieLampEffect, InkEffect, SpellOrbEffect,
VortexRingEffect, DragonBreathEffect, ForceFieldEffect, EmitterEffect, TravellingRing,
FluidSolver, LIMITS, patchReflectiveFloor,
} from 'naturegl-magic';Units are metres and seconds. Vectors are THREE.Vector3 or [x, y, z]. Colours are linear [r, g, b] arrays (above 1 = HDR glow), or THREE.Color, '#rrggbb' or 0xrrggbb (sRGB, converted to linear). Options are stored as JSON arrays.
#MagicSystem
MagicSystem.create(options): Promise<MagicSystem>static asyncBuilds the system: allocates the fluid fields for the quality tier, adds the sparks and the glow light to options.scene, loads the preset, then warms up with renderer.compileAsync(scene, camera) when the renderer has it. new MagicSystem(options) does the same without the warm-up.
| Option | Type | Default | |
|---|---|---|---|
renderer | THREE.WebGLRenderer | — | Required. WebGL2 with EXT_color_buffer_float, or the constructor throws |
scene | THREE.Scene | — | Required. Sparks, the glow light and effect meshes are added here |
camera | THREE.PerspectiveCamera | — | Required. Used by render() and composite() |
quality | 'low', 'medium', 'high', 'ultra' | 'high' | See Quality levels |
domain | { min?, width? } | { min: [-2, 0, -2], width: 4 } | Simulation box. Height = 1.5 × width. The bottom is a solid floor; the sides and top are open |
preset | string, object or null | 'portal' | Loaded on creation. null = default params, no effects |
sparks | boolean | true | GPU sparks |
glowLight | boolean | true | The emission-driven PointLight |
glowShadows | boolean | true | The glow light casts shadows (enable renderer.shadowMap yourself) |
#Frame
magic.update(dt): voidmethodAdvances the effects, one fluid step, the sparks, the glow readback and the lights. dt is clamped to 1/120 … 1/30 s, then scaled by params.controls.speed. Offscreen only: it draws nothing on screen.
magic.render(target = null): voidmethodRenders scene with camera into an internal colour + depth target, then composites the volume over it. Call it instead of renderer.render(scene, camera). The output is tone-mapped (ACES) and sRGB-encoded, to target or the canvas.
magic.composite(inputColor, inputDepth, target = null): voidmethodThe lower-level entry point. inputColor is a linear HDR colour texture and inputDepth its DepthTexture, both from magic.camera. Colour alpha is the reflectivity mask: 1 − alpha reflects the volume on surfaces at the domain floor. See Rendering into your scene.
magic.resize(): voidmethodMatches the internal targets to the renderer's drawing buffer. Call it after renderer.setSize() or setPixelRatio().
The screen pipeline: scene → volume raymarch at the tier's scale (MRT: colour + transmittance, opacity-weighted depth) → temporal accumulation → depth-aware bilateral upsample and composite (+ heat haze) → 6-level bloom → exposure, hue-preserving ACES, saturation, contrast, vignette → sRGB.
#Presets and params
magic.loadPreset(nameOrObject, { effects = true, clear = true }?): MagicEffect[]methodReplaces params with DEFAULT_PARAMS merged with the preset. With effects, removes every effect and adds the preset's. With clear, clears the fluid. Returns the handles it added. Sets presetName to the name, or 'custom' for an object.
magic.setParams(partial): MagicSystemmethodDeep-merges into params. An effects key is ignored; effects are untouched. Arrays are replaced, not merged. Returns the system.
magic.getParams(): objectmethodA deep copy of params plus effects: [{ type, options, intensity? }], JSON-serialisable. Pass it back to loadPreset().
magic.setQualityLevel(level): voidmethodRebuilds the solver at another tier, which clears the fields. Throws on an unknown level.
magic.setLightsEnabled(on): voidmethodShows or hides the glow light and the effects' lights (the spell orbs').
magic.clear(): voidmethodClears velocity, dye and state, the sparks, the in-flight rings and impulses, and the volume's temporal history.
magic.dispose(): voidmethodRemoves every effect, frees all GPU resources and removes the sparks and the glow light from the scene.
#Effects
magic.addEffect(type, options?): MagicEffectmethodtype is a key of EFFECT_TYPES: 'portal', 'genieLamp', 'ink', 'spellOrb', 'vortexRing', 'dragonBreath', 'forceField' or 'emitter'. options deep-merge onto the class's defaults; intensity is also accepted. Throws on an unknown type. See Effects and handles.
magic.addEmitter(options?): EmitterEffectmethodA generic continuous emitter. See Custom spells.
magic.impulse(position, direction = [0, 1, 0], color = [0.62, 0.3, 1], opts?): voidmethodA one-shot burst. opts and their defaults: radius 0.45, speed 0.6, density 26, glow 8, temperature 0.2, fuel 0, expansion 0.35 (radial blast), duration 0.16, sparks true. At most 4 are in flight; a new one drops the oldest.
magic.getEffect(type): MagicEffect | nullmethodThe first effect of that type.
magic.removeEffect(handle): voidmethodAlso magic.removeAllEffects().
#Properties
| Property | |
|---|---|
params | The current MagicParams. Mutate it directly or use setParams |
effects | The live effect handles |
presetName | The last preset loaded |
glow | GlowProbe: position, color, power, smoke, light (PointLight), intensity (light gain) |
glowLight | The same as glow.light |
sparks | Sparks: enabled, points (the THREE.Points in your scene) |
solver | FluidSolver: NX NY NZ H min max, fields { velocity, state, dye, light }, toGrid(v) |
stats | { grid, voxels, steps, quality, label, glowPower, glowColor, smoke, effects } for HUDs |
quality, qualitySettings | The tier name and its settings |
domain | { min: Vector3, width } |
time | Simulation time (s), after the speed control |
uniforms | The shared uniform block (advanced) |
#Effect handles
Every effect extends MagicEffect. The per-effect options are on the guide pages: portal and spellOrb, genieLamp and vortexRing, dragonBreath, ink and forceField, emitter.
effect.move(position): thismethodSets the position. InkEffect moves its whole tank (its position is the tank centre). ForceFieldEffect pauses its wander path for wander.holdTime.
effect.retarget(direction | { target }): thismethodAims direction or axis. GenieLampEffect and EmitterEffect turn velocity and keep its speed. Effects without a direction ignore it.
effect.setColor(color | null): thismethodOverrides every dye colour of the effect; null restores its palette.
effect.setIntensity(k): thismethodScales glow, density and spark rate. Clamped at 0.
effect.set(partialOptions): thismethodDeep-merges options live, with colours normalised.
effect.remove(): voidmethodRemoves it from the system and frees its meshes and lights.
effect.toJSON(): { type, options, intensity? }methodThe preset form. intensity is omitted when it's 1.
| Member | |
|---|---|
type, options, enabled, intensity | enabled = false pauses it and hides its meshes |
position | A copy of the current position |
colorOverride | The setColor() value, or null |
system | The owning MagicSystem |
#Effect-specific members
| Class | Members |
|---|---|
PortalEffect | colors (getter: the colours in effect) |
GenieLampEffect | puff(), rings |
InkEffect | drop(position?, color?) |
SpellOrbEffect | orbs[i].position, orbs[i].color, meshes, lights |
VortexRingEffect | fire(overrides?), rings (TravellingRing[]) |
DragonBreathEffect | color, strength (getters) |
ForceFieldEffect | velocity, pickTarget, mesh |
#Writing an effect
Subclass MagicEffect, give it static defaults, push sources in update(t, dt, frame) and register it in EFFECT_TYPES. The optional hooks are onReset(), onDisabled() and dispose(). See Custom spells.
#Other exports
| Export | |
|---|---|
PRESETS | { portal, genieLamp, ink, spellOrbs, vortexRings, dragonBreath, forceField } |
getPresetParams(nameOrPartial) | A deep-cloned DEFAULT_PARAMS merged with the preset, including its effects array |
DEFAULT_PARAMS | See Parameters |
QUALITY_LEVELS | { n, steps, scale, jacobi, lsteps, fsteps, maccormack, label } per tier. See Quality levels |
EFFECT_TYPES | Type name → effect class. Add your own |
LIMITS | { emitters: 8, impulses: 4, rings: 4, swirls: 2, spawners: 4 } (frozen) |
TravellingRing | The state of one ring in flight: center, axis, age, alive, step(dt, intensity) |
patchReflectiveFloor(material, { strength = 1, roughnessScale = 1.25 }?) | Makes the material write the reflection mask. Returns the strength uniform { value }. See reflections |
#FluidSolver
MagicSystem owns one. Use it directly only for a custom renderer.
new FluidSolver(renderer, pass, uniforms, qualitySettings, domain)classThe dye-carrying solver on an N × 1.5N × N grid stored as a 2D atlas. step() advances it by the shared dt uniform, reset() clears it, fields returns the current velocity, state, dye and light textures, toGrid(v) converts world to grid coordinates, defines holds the GLSL layout defines for your own samplers, and dispose() frees it.