NatureGL Firev1.0.0

Guides

Emitters

An emitter injects fuel, heat and smoke into the grid. It can be a point, a sphere, a box or the surface of any mesh. Every one is a live handle you can move, retune, disable or remove.

The mesh preset: a torus knot in the demo's shape slot. The demo rotates the mesh, and the emitter follows its world matrix every frame.

#Add one

js
const burner = fire.addEmitter({
  type: 'sphere',
  position: [0, 0.6, 0],   // world metres
  radius: 0.3,
  fuel: 60,                // fuel per second
  temperature: 1.3,        // what it injects is ignited at this temperature
  smoke: 0.6,              // smoke per second (× params.smoke)
});

burner.position.x += 0.5;        // move it: every field is live
burner.set({ fuel: 90, velocity: [0, 3, 0] });
burner.enabled = false;          // pause it
burner.remove();                 // or fire.removeEmitter(burner)

loadPreset() replaces every emitter, including ones you added. Call addEmitter() after loading a preset, or build the fire from preset: null and add your own. fire.clearEmitters() removes them all.

#Emitter types

typeShapeNotes
'point'Small sphereradius defaults to 0.15 m
'sphere'Sphere of radiusThe default type. radius defaults to 0.3 m
'box'Box of sizeradius is the edge softness here and defaults to 0.12 m
'mesh'The surface of a meshSurface samples splatted into the grid every frame

Up to 8 point, sphere and box emitters are simulated at once, and any number of mesh emitters. Extra analytic emitters are skipped.

#Source fields

All of these are fields on the handle, with the defaults from EMITTER_DEFAULTS.

FieldDefault
fuel60Fuel injected per second. More fuel means a bigger flame
temperature1.3Ignition temperature of what it injects: about 0.8 smoulders, 1.4 is a torch and 1.6 a blast
smoke0.6Smoke per second, multiplied by params.smoke
flicker0.50 is a steady source. At 1 the injection is fully gated by noise, which breaks the flame into licks
velocity(0, 1.6, 0)Jet velocity in m/s
velocityBlend0.3How hard the gas is forced toward velocity. 0.95 is a hard jet
inheritVelocity0Fraction of the emitter's measured motion added to velocity
embers0.5Ember spawn rate, 0 for none
emberOffset, emberRadius(0, 0.2, 0), 0.3Where the embers spawn, relative to the emitter

#Jets

A jet is an emitter with a strong velocity and a velocityBlend near 1. The jet preset fires a 7.5 m/s nozzle up and to the right into a sphere obstacle:

js
const dir = new THREE.Vector3(0.88, 0.47, 0).normalize();
fire.addEmitter({
  type: 'sphere', position: [-1.45, 0.62, 0], radius: 0.2,
  fuel: 60, temperature: 1.45, smoke: 0.8, flicker: 0.3,
  velocity: dir.multiplyScalar(7.5), velocityBlend: 0.95,
  embers: 2.5,
});
jet: the forced velocity carries the flame sideways until it splashes over the obstacle.

#Moving emitters

Each frame the emitter measures its own world velocity into emitter.motion, clamped to 8 m/s. inheritVelocity passes a fraction of that on to the gas, so a waved torch leaves a trail instead of a flame that stays upright.

js
const torch = fire.addEmitter({
  type: 'sphere', position: [0, 1.6, 0], radius: 0.28,
  fuel: 70, temperature: 1.4, velocity: [0, 1.4, 0], velocityBlend: 0.35,
  inheritVelocity: 0.6,
});

renderer.setAnimationLoop(() => {
  torch.position.copy(hand.getWorldPosition(tmp));   // follow anything
  fire.update(dt);
  fire.render();
});
The torch preset. The demo walks the anchor on a figure-eight, and inheritVelocity: 0.6 bends the flame back along the path.

#Mesh emitters

A mesh emitter samples points on a mesh surface once (with normals) and splats them into the grid every frame, using the mesh's current world matrix. Move, rotate or scale the mesh and the fire follows.

js
const statue = scene.getObjectByName('statue');
fire.addEmitter({
  type: 'mesh',
  mesh: statue,            // any THREE.Mesh with geometry
  fuel: 9, temperature: 1.1, smoke: 0.15,
  samples: 16000,          // surface samples
  weight: 0.6,             // strength
  push: 7,                 // outward + upward push along the normals
  filter: (p, n) => n.y > -0.4,   // optional: skip downward-facing samples
});

Without a scene mesh, pass geometry. It's a BufferGeometry, or one of the built-ins 'teepee', 'torusKnot', 'torus' and 'icosahedron'. The geometry stays invisible and is placed at position.

js
fire.addEmitter({ type: 'mesh', geometry: 'torusKnot', position: [0, 1.65, 0], samples: 26000, weight: 0.45 });

#Preset mesh slots

Presets refer to meshes by slot name, so the same preset file works with your mesh or with a built-in fallback. campfire uses the logs slot (fallback teepee), and mesh uses shape (fallback torusKnot).

js
const fire = await FireSystem.create({ renderer, scene, camera, meshes: { logs: myLogs } });
fire.setMesh('shape', myStatue);                         // register later
fire.loadPreset('mesh', { meshes: { shape: myStatue } }); // or per load

loadPreset() caches surface samples per geometry and sample count, so switching back and forth between presets doesn't resample.