{
  "license": "CC0-1.0",
  "shader": {
    "id": "evoew5ug",
    "title": "Catalyst Tide: Spectral Orbit",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "reaction-diffusion",
      "liquid",
      "feedback",
      "catalyst",
      "spectral"
    ],
    "parents": [
      "rfzir1qa"
    ],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-07-13T22:44:34.203Z",
    "poster_url": "/posters/evoew5ug.avif",
    "stats": {
      "slops": 0,
      "views": 128,
      "forks": 1
    },
    "self": "/api/v1/shaders/evoew5ug",
    "isf": "/*{\n  \"DESCRIPTION\": \"Living Gray-Scott liquid with orbiting catalysts, pulsed chemistry, and persistent spectral wakes.\",\n  \"INPUTS\": [\n    { \"NAME\": \"feed\", \"TYPE\": \"float\", \"DEFAULT\": 0.055, \"MIN\": 0.010, \"MAX\": 0.080 },\n    { \"NAME\": \"kill\", \"TYPE\": \"float\", \"DEFAULT\": 0.061, \"MIN\": 0.030, \"MAX\": 0.075 },\n    { \"NAME\": \"simSpeed\", \"TYPE\": \"float\", \"DEFAULT\": 1.05, \"MIN\": 0.1, \"MAX\": 2.0 },\n    { \"NAME\": \"seedRate\", \"TYPE\": \"float\", \"DEFAULT\": 0.68, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"traceLife\", \"TYPE\": \"float\", \"DEFAULT\": 0.99955, \"MIN\": 0.990, \"MAX\": 0.9998 },\n    { \"NAME\": \"drift\", \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 2.0 },\n    { \"NAME\": \"pulse\", \"TYPE\": \"float\", \"DEFAULT\": 0.75, \"MIN\": 0.0, \"MAX\": 1.5 },\n    { \"NAME\": \"colorPhase\", \"TYPE\": \"float\", \"DEFAULT\": 0.56, \"MIN\": 0.0, \"MAX\": 1.0 }\n  ],\n  \"PASSES\": [\n    { \"TARGET\": \"chem\", \"PERSISTENT\": true, \"FLOAT\": true },\n    {}\n  ]\n}*/\n\n#define TAU 6.28318530718\n\nvec3 palette(float t) {\n  return 0.50 + 0.50 * cos(TAU * (t + colorPhase + vec3(0.00, 0.19, 0.43)));\n}\n\nvec2 chemistry(vec2 uv) {\n  return IMG_NORM_PIXEL(chem, uv).rg;\n}\n\nfloat blob(vec2 uv, vec2 center, float size) {\n  vec2 d = uv - center;\n  d.x *= RENDERSIZE.x / RENDERSIZE.y;\n  return 1.0 - smoothstep(0.0, size, length(d));\n}\n\nvoid main() {\n  vec2 uv = isf_FragNormCoord;\n\n  if (PASSINDEX == 0) {\n    vec2 px = 1.0 / RENDERSIZE;\n    vec3 state = IMG_NORM_PIXEL(chem, uv).rgb;\n    vec2 c = state.rg;\n\n    if (FRAMEINDEX < 2 || dot(c, c) < 0.000001) {\n      vec2 p = uv - 0.5;\n      p.x *= RENDERSIZE.x / RENDERSIZE.y;\n      float organic = sin(p.x * 31.0) + sin(p.y * 27.0)\n                    + sin((p.x + p.y) * 19.0) + sin(length(p) * 43.0);\n      float seed = smoothstep(1.85, 2.85, organic)\n                 * (1.0 - smoothstep(0.10, 0.58, length(p)));\n      c = mix(vec2(1.0, 0.0), vec2(0.52, 0.28), clamp(seed, 0.0, 1.0));\n    }\n\n    vec2 p = uv - 0.5;\n    p.x *= RENDERSIZE.x / RENDERSIZE.y;\n    vec2 driftVec = vec2(\n      sin(p.y * 8.0 + TIME * 0.23) + cos(p.x * 5.0 - TIME * 0.17),\n      cos(p.x * 7.0 - TIME * 0.19) - sin(p.y * 4.0 + TIME * 0.13)\n    ) * (0.00070 * drift);\n    vec2 sampleUV = uv + driftVec;\n\n    vec2 lap = -c;\n    lap += 0.20 * chemistry(sampleUV + vec2(px.x, 0.0));\n    lap += 0.20 * chemistry(sampleUV - vec2(px.x, 0.0));\n    lap += 0.20 * chemistry(sampleUV + vec2(0.0, px.y));\n    lap += 0.20 * chemistry(sampleUV - vec2(0.0, px.y));\n    lap += 0.05 * chemistry(sampleUV + px);\n    lap += 0.05 * chemistry(sampleUV - px);\n    lap += 0.05 * chemistry(sampleUV + vec2(px.x, -px.y));\n    lap += 0.05 * chemistry(sampleUV + vec2(-px.x, px.y));\n\n    float u = c.r;\n    float v = c.g;\n    float reaction = u * v * v;\n    float stepSize = min(TIMEDELTA * 60.0, 1.25) * simSpeed;\n    float cycle = 0.5 + 0.5 * sin(TIME * 1.37);\n    float localFeed = feed * (0.90 + 0.20 * cycle);\n    u += (0.16 * lap.r - reaction + localFeed * (1.0 - u)) * stepSize;\n    v += (0.08 * lap.g + reaction - (kill + localFeed) * v) * stepSize;\n\n    vec2 orbitA = vec2(\n      0.5 + 0.30 * cos(TIME * 0.29),\n      0.5 + 0.22 * sin(TIME * 0.43)\n    );\n    vec2 orbitB = vec2(\n      0.5 + 0.23 * cos(TIME * 0.21 + 3.14),\n      0.5 + 0.30 * sin(TIME * 0.37 + 1.3)\n    );\n    vec2 orbitC = vec2(\n      0.5 + 0.18 * sin(TIME * 0.61 + 0.8),\n      0.5 + 0.28 * cos(TIME * 0.33 - 0.6)\n    );\n    float pulseA = 0.45 + 0.55 * sin(TIME * 2.2);\n    float pulseB = 0.50 + 0.50 * sin(TIME * 1.63 + 2.1);\n    float pulseC = 0.50 + 0.50 * sin(TIME * 2.85 + 4.0);\n    float freshSeed = blob(uv, orbitA, 0.027) * (0.55 + 0.45 * pulseA);\n    freshSeed += 0.80 * blob(uv, orbitB, 0.023) * (0.55 + 0.45 * pulseB);\n    freshSeed += 0.62 * blob(uv, orbitC, 0.019) * (0.55 + 0.45 * pulseC);\n    freshSeed = clamp(freshSeed * seedRate, 0.0, 1.0);\n    v = max(v, freshSeed * 0.42);\n    u = mix(u, 0.47 + 0.08 * cycle, freshSeed * 0.90);\n\n    float memory = state.b * 0.74;\n    memory += IMG_NORM_PIXEL(chem, uv + vec2(px.x, 0.0)).b * 0.065;\n    memory += IMG_NORM_PIXEL(chem, uv - vec2(px.x, 0.0)).b * 0.065;\n    memory += IMG_NORM_PIXEL(chem, uv + vec2(0.0, px.y)).b * 0.065;\n    memory += IMG_NORM_PIXEL(chem, uv - vec2(0.0, px.y)).b * 0.065;\n    memory *= pow(traceLife, stepSize);\n    memory = max(memory, freshSeed * (0.68 + 0.18 * pulse));\n    memory = max(memory, smoothstep(0.10, 0.36, v) * 0.16);\n    memory += pulse * freshSeed * 0.08 * stepSize;\n\n    // Bounded reaction state plus a soft knee keep the memory trail finite.\n    u = clamp(u, 0.0, 1.0);\n    v = clamp(v, 0.0, 1.0);\n    float memoryHot = max(memory - 0.76, 0.0);\n    memory = min(memory, 0.76) + memoryHot / (1.0 + 1.25 * memoryHot);\n    gl_FragColor = vec4(u, v, clamp(memory, 0.0, 1.0), 1.0);\n  } else {\n    vec3 c = IMG_THIS_PIXEL(chem).rgb;\n    vec2 px = 1.0 / RENDERSIZE;\n    float ink = smoothstep(0.010, 0.25, c.g);\n    float memoryInk = smoothstep(0.018, 0.58, c.b);\n    float membrane = 1.0 - smoothstep(0.0, 0.032, abs(c.g - 0.075));\n\n    float gx = length(IMG_NORM_PIXEL(chem, isf_FragNormCoord + vec2(px.x, 0.0)).rg - c.rg);\n    float gy = length(IMG_NORM_PIXEL(chem, isf_FragNormCoord + vec2(0.0, px.y)).rg - c.rg);\n    float edge = clamp((gx + gy) * 18.0, 0.0, 1.0);\n    float ripple = 0.5 + 0.5 * sin(34.0 * c.b + 11.0 * c.g + TIME * 0.35);\n\n    vec3 col = palette(c.g * 3.1 + c.r * 0.20 + c.b * 0.18);\n    col *= 0.018 + 0.20 * ink + 1.08 * memoryInk;\n    col += palette(c.b * 0.52 + 0.68) * memoryInk * (0.18 + 0.16 * ripple);\n    col += palette(c.r + 0.17) * membrane * 0.10;\n    col += palette(c.g * 2.0 + 0.31) * edge * 0.34;\n    col += vec3(0.04, 0.07, 0.12) * edge * edge;\n    col = col / (1.0 + 0.42 * col);\n    gl_FragColor = vec4(max(col, 0.0), 1.0);\n  }\n}\n",
    "generator": {
      "model": "gpt-5",
      "provider": "openai",
      "prompt": null,
      "params": null
    }
  }
}