Fire Dissolve / Burn Away Effect
Been playing a ton of Dead Cells lately and that burn-away death animation got stuck in my head. Had to try recreating that kind of dissolve effect where the edges glow hot like embers before crumbling to nothing.
The basic idea: use layered noise as a threshold mask, then anything near the dissolve boundary gets that hot orange/white edge glow. Everything below the threshold is transparent (burned away), everything above is the original surface. The tricky bit was getting the ember edge to look right — I ended up stacking two emission bands at different widths so you get a white-hot core fading out to deep orange/red. Looks way better than a single color ramp.
I'm driving the dissolve with iTime so it just loops. The noise is a basic hash-based value noise with a couple of octaves of fbm. Nothing fancy but it gives that organic, papery burn pattern.
precision mediump float;
uniform vec2 iResolution;
uniform float iTime;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
float fbm(vec2 p) {
float val = 0.0;
float amp = 0.5;
float freq = 1.0;
for (int i = 0; i < 5; i++) {
val += amp * noise(p * freq);
freq *= 2.0;
amp *= 0.5;
}
return val;
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
vec2 nUV = uv * 4.0;
// base noise pattern for the burn mask
float n = fbm(nUV + vec2(0.3, -0.5) * iTime * 0.15);
n += 0.3 * fbm(nUV * 3.0 + vec2(1.7, 2.3));
n = n * 0.7;
// dissolve threshold — loops over time
float t = fract(iTime * 0.12);
// remap so it fully burns then fully reforms
float threshold = t < 0.5 ? t * 2.0 : (1.0 - t) * 2.0;
threshold = threshold * 1.4 - 0.2; // overshoot so we get full burn/full solid
float diff = n - threshold;
// the surface color — a simple gradient to stand in for geometry
vec3 surfCol = mix(vec3(0.15, 0.12, 0.1), vec3(0.45, 0.38, 0.32), uv.y);
// add some texture variation
surfCol *= 0.85 + 0.15 * noise(nUV * 6.0);
// ember edge emission
float edgeWidth = 0.08;
float narrowEdge = smoothstep(0.0, edgeWidth * 0.3, diff);
float wideEdge = smoothstep(0.0, edgeWidth, diff);
// hot core: white-yellow, outer: orange-red
vec3 emberCore = vec3(1.0, 0.95, 0.7);
vec3 emberOuter = vec3(1.0, 0.35, 0.05);
vec3 emberDeep = vec3(0.6, 0.05, 0.0);
vec3 emberCol = mix(emberCore, emberOuter, narrowEdge);
emberCol = mix(emberCol, emberDeep, wideEdge * 0.6);
// ember glow intensity — strongest right at the boundary
float glowIntensity = 1.0 - smoothstep(0.0, edgeWidth * 1.2, diff);
glowIntensity *= glowIntensity; // sharpen falloff
// flicker some sparks near the edge
float sparkNoise = noise(nUV * 20.0 + iTime * 3.0);
float sparks = step(0.92, sparkNoise) * glowIntensity * 1.5;
// composite
vec3 col = surfCol;
col = mix(col, emberCol, glowIntensity);
col += sparks * vec3(1.0, 0.8, 0.3);
// alpha: burned region is transparent
float alpha = smoothstep(-0.005, 0.01, diff);
// slight ambient glow behind the burn front (fake scatter)
float behindGlow = (1.0 - alpha) * glowIntensity * 0.4;
col += behindGlow * vec3(0.8, 0.2, 0.0);
// darken the very edge (char)
float charBand = smoothstep(edgeWidth * 0.5, edgeWidth * 0.15, diff) * smoothstep(0.0, 0.01, diff);
col = mix(col, vec3(0.05, 0.02, 0.0), charBand * 0.5);
gl_FragColor = vec4(col, alpha);
}Some things I noticed while tweaking:
- The edge width matters a lot. Too thin and it looks like a clean cut with a color border. Too wide and it just looks like a gradient. Somewhere around 0.06-0.1 gives that nice papery char look.
- Stacking the narrow white-hot band inside the wider orange band is what sells it. Single color edge looks flat.
- The sparks are dead simple (just thresholded high-frequency noise) but they add a surprising amount of life.
- I'm cheating with the char darkening band — it's not physically correct at all but it reads well.
Would love to see if anyone has a better approach to the spark/ember particles. Right now they're just screen-space noise which works okay but doesn't give you those trailing particle arcs you see in the really nice implementations. Might need actual particle simulation for that.
Feel free to grab this and mess with it. Swap in your own surface color or pipe a texture through for the surface and it should work as a generic dissolve transition.