Guides
Fuel, heat and smoke
Every voxel stores temperature, fuel and smoke. Emitters add fuel and heat, hot fuel burns, and burning releases more heat, soot and volume. The flame you see is gas above the glow threshold, coloured by a blackbody ramp.
#The combustion model
The state pass runs once per simulation step, for every voxel. After the three fields are advected:
- Inject. Emitters and bursts add fuel and smoke, and raise the temperature to at least their own
temperature. Emitters ignite what they inject. - Burn. Fuel burns at
params.burnRate, but only where the gas is hot enough. The ignition ramp runs fromT = 0.3to0.55. - Release. Each unit of burnt fuel adds heat (×1.25), soot (×0.55 ×
params.smoke) and volume. The volume becomes the combustion expansion in the pressure solve (×params.expansion). - Cool and fade. Temperature loses
0.3·T + 0.28·T³per second, scaled byparams.cooling. Smoke and fuel dissipate slowly, and everything fades near the side and top walls, so nothing piles up at the edge of the box.
The fields are clamped to temperature 0–1.7, fuel 0–12 and smoke 0–6. Hot gas rises with 9.5 × params.buoyancy × T, and smoke weighs it down a little. Obstacle cells are always empty.
#The dials
These are the seven sliders in the demo. All are multipliers with a default of 1.
| Param | Demo range | Turn it up for |
|---|---|---|
wind | −4 … 4 | Horizontal wind in m/s (default 0). See Wind and obstacles |
turbulence | 0 … 2 | More noise forcing and stronger vorticity confinement: a busier, curlier flame |
cooling | 0.2 … 3 | Shorter flames that turn to smoke sooner |
expansion | 0 … 3 | More volume from burning: flames that spread and puff, bigger bursts |
buoyancy | 0 … 3 | Faster-rising, taller flames |
smoke | 0 … 2.5 | More soot from emitters, bursts and burning |
intensity | 0.2 … 3 | Brighter flame emission and a brighter fire light |
fire.setParams({ cooling: 1.4, buoyancy: 1.2 }); // merge
fire.params.smoke = 0.4; // params is live: mutate it directlyplume: a 0.8 emitter temperature, smoke: 1.8 and an emitter smoke rate of 7. It smoulders more than it burns.campfire: a 1.1 emitter temperature, smoke: 0.8 and a low emitter smoke rate. The flames stay clean and bright.#Flame look
The raymarcher turns the fields into colour. None of these affect the simulation.
| Param | Default | |
|---|---|---|
emission | 3.2 | Base flame emission, × intensity |
flameThreshold | 0.34 | Temperature where gas starts to glow |
flameAbsorption | 1.5 | Flame extinction. Higher values make the cores opaque |
kelvinBase, kelvinScale | 1100, 1800 | Maps T to kelvin for the blackbody ramp |
blueBase | 1 | The blue premixed glow where fuel is burning |
licks | 0.55 | Sub-grid flame tongues: two octaves of rising noise at the hot edge |
detail | 2.8 | Sub-grid noise displacement in cells, scaled with the grid |
smokeDensity | 2.6 | Smoke extinction |
smokeAlbedo | 0.62 | Single-scattering albedo of the smoke |
smokeColor | '#ffffff' | Smoke tint |
Hot soot is drawn thin. Smoke only shows at full density once it has cooled, so you don't get dark smoke inside the flame.
#The blackbody ramp
Flame colour comes from a lookup texture that integrates Planck's law from 380 to 780 nm against the CIE 1931 observer (the Wyman, Sloan and Shirley multi-lobe fit) and converts to linear sRGB. It covers 500 K to 4000 K. The fire light uses the same ramp, so the light always matches the flame. You can use it yourself:
import { blackbodyRGB, kelvinToColor } from 'naturegl-fire';
blackbodyRGB(1800); // [r, g, b, Y]: normalised linear sRGB + relative luminance
kelvinToColor(2400, lampLight.color); // write straight into a THREE.Color#Smoke lighting
The light-volume pass stores three things per voxel. They are the transmittance toward the key light, the shadowed and distance-attenuated fire light, and the transmittance straight up. The upward term self-shadows the ambient light, so the inside of thick smoke darkens instead of glowing flat.
| Param | Default | |
|---|---|---|
selfShadow | true | Smoke and flame shadow themselves in the light volume. Off, all three terms are unshadowed |
shadowDensity | 2.2 | Extinction used for that shadowing |
The key light is a direction plus a colour, set with setLighting(). The demo points it at its moon. See In your scene.