open .fs {
"DESCRIPTION" : "Ember flow — sparks streaked along a rising curl-noise heat field, traced by line-integral convolution." ,
"CREDIT" : "claude-opus-4-8" ,
"INPUTS" : [
{ "NAME" : "scale" , "TYPE" : "float" , "DEFAULT" : 2.4 , "MIN" : 1.0 , "MAX" : 6.0 } ,
{ "NAME" : "buoy" , "TYPE" : "float" , "DEFAULT" : 0.8 , "MIN" : 0.0 , "MAX" : 2.0 } ,
{ "NAME" : "contrast" , "TYPE" : "float" , "DEFAULT" : 1.6 , "MIN" : 0.6 , "MAX" : 3.0 } ,
{ "NAME" : "heat" , "TYPE" : "float" , "DEFAULT" : 1.0 , "MIN" : 0.4 , "MAX" : 1.8 }
]
}
float hash (vec2 p){ p = fract (p * vec2 (123.34 , 456.21 )); p += dot (p, p + 45.32 ); return fract (p.x * p.y); }
float noise (vec2 p){
vec2 i = floor (p), f = fract (p); f = f * f * (3.0 - 2.0 * f);
float a = hash (i), b = hash (i + vec2 (1.0 , 0.0 )), c = hash (i + vec2 (0.0 , 1.0 )), 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 s = 0.0 , a = 0.5 ; for (int i = 0 ; i < 4 ; i++){ s += a * noise (p); p = p * 2.0 + 3.0 ; a *= 0.5 ; } return s; }
vec2 curl (vec2 p){
float e = 0.01 ;
float a = fbm (p + vec2 (0.0 , e)), b = fbm (p - vec2 (0.0 , e));
float c = fbm (p + vec2 (e, 0.0 )), d = fbm (p - vec2 (e, 0.0 ));
return vec2 (a - b, d - c) / (2.0 * e);
}
vec2 field (vec2 q, float t){
vec2 v = curl (q * 0.8 + vec2 (0.0 , -t * 0.3 )) * 0.8 ;
v += vec2 (0.0 , buoy);
return v;
}
vec3 fire (float h){
h = clamp (h, 0.0 , 1.0 );
vec3 c = mix (vec3 (0.0 ), vec3 (0.55 , 0.04 , 0.0 ), smoothstep (0.0 , 0.3 , h));
c = mix (c, vec3 (1.0 , 0.35 , 0.0 ), smoothstep (0.3 , 0.6 , h));
c = mix (c, vec3 (1.0 , 0.85 , 0.35 ), smoothstep (0.6 , 0.86 , h));
c = mix (c, vec3 (1.0 , 1.0 , 0.92 ), smoothstep (0.86 , 1.0 , h));
return c;
}
void main (){
vec2 uv = (gl_FragCoord .xy - 0.5 * RENDERSIZE ) / RENDERSIZE .y;
vec2 q = uv * scale;
float t = TIME ;
float acc = 0.0 , wsum = 0.0 ;
vec2 pf = q, pb = q;
for (int i = 0 ; i < 18 ; i++){
float wi = 1.0 - float (i) / 18.0 ;
vec2 ff = field (pf, t), fb = field (pb, t);
pf += ff / (length (ff) + 1e-4 ) * 0.03 ;
pb -= fb / (length (fb) + 1e-4 ) * 0.03 ;
acc += wi * noise (pf * 10.0 + vec2 (0.0 , t * 2.0 ));
acc += wi * noise (pb * 10.0 + vec2 (0.0 , t * 2.0 ));
wsum += 2.0 * wi;
}
acc /= wsum;
float h = pow (clamp ((acc - 0.5 ) * contrast + 0.5 , 0.0 , 1.0 ), 1.4 ) * heat;
h *= 0.7 + 0.5 * smoothstep (0.8 , -0.6 , uv.y);
vec3 col = fire (h);
col += vec3 (0.1 , 0.2 , 0.6 ) * smoothstep (0.93 , 1.0 , h) * 0.5 ;
vec2 sp = q * 3.0 ; sp.y += t * 1.6 ;
vec2 sid = floor (sp);
float sh = hash (sid);
vec2 sc = sid + 0.5 + 0.32 * vec2 (sin (sh * 30.0 + t * 2.0 ), cos (sh * 23.0 + t));
float sd = length (sp - sc);
float spark = smoothstep (0.11 , 0.0 , sd) * step (0.86 , sh) * (0.5 + 0.5 * sin (t * 6.0 + sh * 50.0 ));
spark *= smoothstep (0.15 , 0.5 , h);
col += spark * vec3 (1.0 , 0.8 , 0.45 ) * 1.6 ;
col = mix (col, col * vec3 (0.8 , 0.55 , 0.5 ) + vec3 (0.02 , 0.012 , 0.015 ),
smoothstep (0.1 , 0.95 , uv.y) * (1.0 - h) * 0.6 );
col *= 1.0 - 0.32 * dot (uv, uv);
col = pow (max (col, 0.0 ), vec3 (0.9 ));
gl_FragColor = vec4 (col, 1.0 );
}