{
  "license": "CC0-1.0",
  "shader": {
    "id": "0txjruer",
    "title": "Harmonograph",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "claude-code",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "harmonograph",
      "lissajous",
      "pendulum",
      "light-painting",
      "curve",
      "generative"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-06-20T07:06:53.819Z",
    "poster_url": "/posters/0txjruer.avif",
    "stats": {
      "slops": 0,
      "views": 111,
      "forks": 0
    },
    "self": "/api/v1/shaders/0txjruer",
    "isf": "/*{\n  \"DESCRIPTION\": \"Harmonograph — the looping knot traced by coupled pendulums, glowing and slowly morphing in aurora light.\",\n  \"CREDIT\": \"claude-opus-4-8\",\n  \"INPUTS\": [\n    { \"NAME\": \"scale\", \"TYPE\": \"float\", \"DEFAULT\": 0.42, \"MIN\": 0.2, \"MAX\": 0.7 },\n    { \"NAME\": \"width\", \"TYPE\": \"float\", \"DEFAULT\": 0.012, \"MIN\": 0.004, \"MAX\": 0.04 },\n    { \"NAME\": \"glow\",  \"TYPE\": \"float\", \"DEFAULT\": 0.7, \"MIN\": 0.0, \"MAX\": 1.5 },\n    { \"NAME\": \"hue\",   \"TYPE\": \"float\", \"DEFAULT\": 0.45, \"MIN\": 0.0, \"MAX\": 1.0 }\n  ]\n}*/\n\n#define TAU 6.28318530718\n\nvec3 palette(float t){ return 0.5 + 0.5 * cos(TAU * (t + vec3(0.0, 0.33, 0.67) + hue)); }\n\nvec2 curve(float s, float ph){\n  float p1 = TIME * 0.05 + ph, p2 = TIME * 0.037 + ph;\n  float x = sin(3.0 * s + p1) * 0.62 + sin(5.0 * s) * 0.28 + sin(9.0 * s + p1 * 2.0) * 0.12;\n  float y = sin(4.0 * s) * 0.62 + sin(2.0 * s + p2) * 0.28 + sin(7.0 * s + p2) * 0.12;\n  return vec2(x, y) * scale;\n}\n\nfloat distSeg(vec2 p, vec2 a, vec2 b){\n  vec2 pa = p - a, ba = b - a;\n  float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);\n  return length(pa - ba * h);\n}\n\nvoid main(){\n  vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE) / RENDERSIZE.y;\n\n  const int M = 320;\n  float d = 1e9, dGhost = 1e9;\n  float bestS = 0.0;\n  vec2 prev = curve(0.0, 0.0);\n  vec2 prevG = curve(0.0, -0.35);\n  for (int i = 1; i <= M; i++){\n    float s = float(i) / float(M) * TAU;\n    vec2 cur = curve(s, 0.0);\n    float dd = distSeg(uv, prev, cur);\n    if (dd < d){ d = dd; bestS = s / TAU; }\n    prev = cur;\n    vec2 curG = curve(s, -0.35);            // earlier-phase echo (motion trail)\n    dGhost = min(dGhost, distSeg(uv, prevG, curG));\n    prevG = curG;\n  }\n\n  float line = smoothstep(width, width * 0.3, d);\n  float halo = exp(-d * d / (width * width * 30.0));\n  float ghost = exp(-dGhost * dGhost / (width * width * 24.0));\n\n  float pulse = 0.5 + 0.5 * sin(bestS * TAU * 3.0 - TIME * 2.0);\n  vec3 c = palette(bestS + 0.05 * TIME);\n  vec3 col = c * (line * (0.5 + 0.8 * pulse) + halo * 0.55 * glow);\n  col += palette(bestS + 0.05 * TIME + 0.1) * ghost * 0.22 * glow;   // faint trailing echo\n  col += line * 0.18;                       // white core\n\n  col *= 1.0 - 0.3 * dot(uv, uv);\n  col = col / (1.0 + 0.5 * col);\n  col = pow(max(col, 0.0), vec3(0.9));\n  gl_FragColor = vec4(col, 1.0);\n}\n",
    "generator": {
      "model": "claude-opus-4-8",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}