open .fs {
"DESCRIPTION" : "Living Gray-Scott liquid with orbiting catalysts, pulsed chemistry, and persistent spectral wakes." ,
"INPUTS" : [
{ "NAME" : "feed" , "TYPE" : "float" , "DEFAULT" : 0.055 , "MIN" : 0.010 , "MAX" : 0.080 } ,
{ "NAME" : "kill" , "TYPE" : "float" , "DEFAULT" : 0.061 , "MIN" : 0.030 , "MAX" : 0.075 } ,
{ "NAME" : "simSpeed" , "TYPE" : "float" , "DEFAULT" : 1.05 , "MIN" : 0.1 , "MAX" : 2.0 } ,
{ "NAME" : "seedRate" , "TYPE" : "float" , "DEFAULT" : 0.68 , "MIN" : 0.0 , "MAX" : 1.0 } ,
{ "NAME" : "traceLife" , "TYPE" : "float" , "DEFAULT" : 0.99955 , "MIN" : 0.990 , "MAX" : 0.9998 } ,
{ "NAME" : "drift" , "TYPE" : "float" , "DEFAULT" : 1.0 , "MIN" : 0.0 , "MAX" : 2.0 } ,
{ "NAME" : "pulse" , "TYPE" : "float" , "DEFAULT" : 0.75 , "MIN" : 0.0 , "MAX" : 1.5 } ,
{ "NAME" : "colorPhase" , "TYPE" : "float" , "DEFAULT" : 0.56 , "MIN" : 0.0 , "MAX" : 1.0 }
] ,
"PASSES" : [
{ "TARGET" : "chem" , "PERSISTENT" : true , "FLOAT" : true } ,
{ }
]
}
#define TAU 6.28318530718
vec3 palette (float t) {
return 0.50 + 0.50 * cos (TAU * (t + colorPhase + vec3 (0.00 , 0.19 , 0.43 )));
}
vec2 chemistry (vec2 uv) {
return IMG_NORM_PIXEL (chem, uv).rg;
}
float blob (vec2 uv, vec2 center, float size) {
vec2 d = uv - center;
d.x *= RENDERSIZE .x / RENDERSIZE .y;
return 1.0 - smoothstep (0.0 , size, length (d));
}
void main () {
vec2 uv = isf_FragNormCoord ;
if (PASSINDEX == 0 ) {
vec2 px = 1.0 / RENDERSIZE ;
vec3 state = IMG_NORM_PIXEL (chem, uv).rgb;
vec2 c = state.rg;
if (FRAMEINDEX < 2 || dot (c, c) < 0.000001 ) {
vec2 p = uv - 0.5 ;
p.x *= RENDERSIZE .x / RENDERSIZE .y;
float organic = sin (p.x * 31.0 ) + sin (p.y * 27.0 )
+ sin ((p.x + p.y) * 19.0 ) + sin (length (p) * 43.0 );
float seed = smoothstep (1.85 , 2.85 , organic)
* (1.0 - smoothstep (0.10 , 0.58 , length (p)));
c = mix (vec2 (1.0 , 0.0 ), vec2 (0.52 , 0.28 ), clamp (seed, 0.0 , 1.0 ));
}
vec2 p = uv - 0.5 ;
p.x *= RENDERSIZE .x / RENDERSIZE .y;
vec2 driftVec = vec2 (
sin (p.y * 8.0 + TIME * 0.23 ) + cos (p.x * 5.0 - TIME * 0.17 ),
cos (p.x * 7.0 - TIME * 0.19 ) - sin (p.y * 4.0 + TIME * 0.13 )
) * (0.00070 * drift);
vec2 sampleUV = uv + driftVec;
vec2 lap = -c;
lap += 0.20 * chemistry (sampleUV + vec2 (px.x, 0.0 ));
lap += 0.20 * chemistry (sampleUV - vec2 (px.x, 0.0 ));
lap += 0.20 * chemistry (sampleUV + vec2 (0.0 , px.y));
lap += 0.20 * chemistry (sampleUV - vec2 (0.0 , px.y));
lap += 0.05 * chemistry (sampleUV + px);
lap += 0.05 * chemistry (sampleUV - px);
lap += 0.05 * chemistry (sampleUV + vec2 (px.x, -px.y));
lap += 0.05 * chemistry (sampleUV + vec2 (-px.x, px.y));
float u = c.r;
float v = c.g;
float reaction = u * v * v;
float stepSize = min (TIMEDELTA * 60.0 , 1.25 ) * simSpeed;
float cycle = 0.5 + 0.5 * sin (TIME * 1.37 );
float localFeed = feed * (0.90 + 0.20 * cycle);
u += (0.16 * lap.r - reaction + localFeed * (1.0 - u)) * stepSize;
v += (0.08 * lap.g + reaction - (kill + localFeed) * v) * stepSize;
vec2 orbitA = vec2 (
0.5 + 0.30 * cos (TIME * 0.29 ),
0.5 + 0.22 * sin (TIME * 0.43 )
);
vec2 orbitB = vec2 (
0.5 + 0.23 * cos (TIME * 0.21 + 3.14 ),
0.5 + 0.30 * sin (TIME * 0.37 + 1.3 )
);
vec2 orbitC = vec2 (
0.5 + 0.18 * sin (TIME * 0.61 + 0.8 ),
0.5 + 0.28 * cos (TIME * 0.33 - 0.6 )
);
float pulseA = 0.45 + 0.55 * sin (TIME * 2.2 );
float pulseB = 0.50 + 0.50 * sin (TIME * 1.63 + 2.1 );
float pulseC = 0.50 + 0.50 * sin (TIME * 2.85 + 4.0 );
float freshSeed = blob (uv, orbitA, 0.027 ) * (0.55 + 0.45 * pulseA);
freshSeed += 0.80 * blob (uv, orbitB, 0.023 ) * (0.55 + 0.45 * pulseB);
freshSeed += 0.62 * blob (uv, orbitC, 0.019 ) * (0.55 + 0.45 * pulseC);
freshSeed = clamp (freshSeed * seedRate, 0.0 , 1.0 );
v = max (v, freshSeed * 0.42 );
u = mix (u, 0.47 + 0.08 * cycle, freshSeed * 0.90 );
float memory = state.b * 0.74 ;
memory += IMG_NORM_PIXEL (chem, uv + vec2 (px.x, 0.0 )).b * 0.065 ;
memory += IMG_NORM_PIXEL (chem, uv - vec2 (px.x, 0.0 )).b * 0.065 ;
memory += IMG_NORM_PIXEL (chem, uv + vec2 (0.0 , px.y)).b * 0.065 ;
memory += IMG_NORM_PIXEL (chem, uv - vec2 (0.0 , px.y)).b * 0.065 ;
memory *= pow (traceLife, stepSize);
memory = max (memory, freshSeed * (0.68 + 0.18 * pulse));
memory = max (memory, smoothstep (0.10 , 0.36 , v) * 0.16 );
memory += pulse * freshSeed * 0.08 * stepSize;
u = clamp (u, 0.0 , 1.0 );
v = clamp (v, 0.0 , 1.0 );
float memoryHot = max (memory - 0.76 , 0.0 );
memory = min (memory, 0.76 ) + memoryHot / (1.0 + 1.25 * memoryHot);
gl_FragColor = vec4 (u, v, clamp (memory, 0.0 , 1.0 ), 1.0 );
} else {
vec3 c = IMG_THIS_PIXEL (chem).rgb;
vec2 px = 1.0 / RENDERSIZE ;
float ink = smoothstep (0.010 , 0.25 , c.g);
float memoryInk = smoothstep (0.018 , 0.58 , c.b);
float membrane = 1.0 - smoothstep (0.0 , 0.032 , abs (c.g - 0.075 ));
float gx = length (IMG_NORM_PIXEL (chem, isf_FragNormCoord + vec2 (px.x, 0.0 )).rg - c.rg);
float gy = length (IMG_NORM_PIXEL (chem, isf_FragNormCoord + vec2 (0.0 , px.y)).rg - c.rg);
float edge = clamp ((gx + gy) * 18.0 , 0.0 , 1.0 );
float ripple = 0.5 + 0.5 * sin (34.0 * c.b + 11.0 * c.g + TIME * 0.35 );
vec3 col = palette (c.g * 3.1 + c.r * 0.20 + c.b * 0.18 );
col *= 0.018 + 0.20 * ink + 1.08 * memoryInk;
col += palette (c.b * 0.52 + 0.68 ) * memoryInk * (0.18 + 0.16 * ripple);
col += palette (c.r + 0.17 ) * membrane * 0.10 ;
col += palette (c.g * 2.0 + 0.31 ) * edge * 0.34 ;
col += vec3 (0.04 , 0.07 , 0.12 ) * edge * edge;
col = col / (1.0 + 0.42 * col);
gl_FragColor = vec4 (max (col, 0.0 ), 1.0 );
}
}