Fire & flames
Noise scrolling upward inside a shaped mask, with the result mapped to a hot palette from dark red to white.
Cards
One live example to tweak, learn and copy.
How it works
A flame is hot gas rising. It is narrow where it starts, it flickers more the higher it goes, and its color says how hot each part is: dark red at the cooling edges, orange, then yellow and near white in the hottest region, the same order a heated piece of metal glows through. A fire shader rebuilds each of those observations with a small piece of math, and every fire effect, from a candle to a torch to a fireball, is the same three pieces arranged differently.
- A shaped mask. A smoothstepA soft threshold. It is 0 below the first edge, 1 above the second, and follows an S-shaped curve with flat ends in between.Fundamental
Smoothstep
A soft threshold. It is 0 below the first edge, 1 above the second, and follows an S-shaped curve with flat ends in between.
t = clamp((x − e0) / (e1 − e0), 0, 1); s = t² (3 − 2t)Sourcesaround a distance gives the flame’s outline: a teardrop for a candle, a wide cone for a campfire, a circle for a fireball. Shaping curves (a square root, a power, one minus the height) make its profile round at the bottom and pointed at the top.- smoothstep (opens in a new tab)Reference
- Smoothstep (opens in a new tab)Reference
- Rising noise. An fBmA sum of several copies of a noise, each at a higher frequency and a lower strength than the last, so the result has detail at many sizes at once, like clouds, smoke or terrain.Fundamental · Noise
Fractal Brownian motion (fBm)
A sum of several copies of a noise, each at a higher frequency and a lower strength than the last, so the result has detail at many sizes at once, like clouds, smoke or terrain.
fbm(p) = Σ gainᵏ · noise(lacunarityᵏ · p)Sourceswhose coordinate scrolls upward with time pushes the mask sideways and breaks it into tongues. Pushing more near the top than at the bottom keeps the base anchored and lets the tip whip around, which is what makes it read as fire rather than as a wobbling shape. - A hot palette. The mask and the noise together give a heat value between 0 and 1. A palette maps it to color from black through red, orange and yellow to white, so the same number drives both the shape and the color, and the colors always stay in the right order.
Over a scene
Fire is light. Drawn opaque it needs a black background, which is how the cards show it first. In a game it goes on a
camera-facing quad or a particle with additive blendingCombining the color a fragment shader outputs with the color already in the framebuffer, instead of replacing it. Transparency, additive glow and multiply all come from it. Blending Combining the color a fragment shader outputs with the color already in the framebuffer, instead of replacing it. Transparency, additive glow and multiply all come from it.result = src × srcFactor + dst × dstFactor: the flame’s color is added to
what is behind it, black adds nothing, so the quad’s edges disappear and overlapping flames brighten each other
instead of covering each other. The shader stays the same; only the blend mode changes.
Further reading
- Fractal Brownian Motion (opens in a new tab)Book
- fBM (opens in a new tab)Article
- Fractional Brownian motion (opens in a new tab)Reference
- Palettes (opens in a new tab)Article
- Colors (opens in a new tab)Book
- Shaping functions (opens in a new tab)Book
- Useful little functions (opens in a new tab)Article
- pow (opens in a new tab)Reference
- Uniforms (opens in a new tab)Book
- Window: requestAnimationFrame() method (opens in a new tab)Docs
- The OpenGL ES Shading Language, version 3.00 (opens in a new tab)Docs
- Blending (opens in a new tab)Article
- WebGLRenderingContext: blendFunc() method (opens in a new tab)Docs
- Spatial shaders (opens in a new tab)Docs
- Flame (opens in a new tab)Reference
- Black-body radiation (opens in a new tab)Reference
- smoothstep (opens in a new tab)Reference
- mix (opens in a new tab)Reference
- CanvasItem shaders (opens in a new tab)Docs
- Shading language (opens in a new tab)Docs