{
  "license": "CC0-1.0",
  "shader": {
    "id": "i5b961fm",
    "title": "Chladni Sand Plate",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop-skill",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "feedback",
      "simulation",
      "generative",
      "multipass"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-07-14T20:54:55.300Z",
    "poster_url": "/posters/i5b961fm.avif",
    "stats": {
      "slops": 0,
      "views": 109,
      "forks": 0
    },
    "self": "/api/v1/shaders/i5b961fm",
    "isf": "/*{\n  \"DESCRIPTION\": \"A virtual Chladni plate: two vibration modes crossfade over time and grains of sand migrate to the nodal lines, accumulating in a persistent feedback buffer like a real resonating plate. The mode field itself loops exactly; the sand keeps quietly settling and never resets.\",\n  \"CREDIT\": \"glslop\",\n  \"CATEGORIES\": [\"Simulation\", \"Feedback\", \"Generative\"],\n  \"INPUTS\": [\n    { \"NAME\": \"rate\",  \"TYPE\": \"float\", \"DEFAULT\": 0.05, \"MIN\": 0.0, \"MAX\": 0.15 },\n    { \"NAME\": \"grain\", \"TYPE\": \"color\", \"DEFAULT\": [1.0, 0.9, 0.72, 1.0] },\n    { \"NAME\": \"plateHue\", \"TYPE\": \"color\", \"DEFAULT\": [0.06, 0.09, 0.2, 1.0] }\n  ],\n  \"PASSES\": [\n    { \"TARGET\": \"sand\", \"PERSISTENT\": true },\n    { }\n  ]\n}*/\n\n#define PI 3.14159265359\n\nfloat chladni(vec2 p, float m, float n){\n  return sin(PI*m*p.x) * sin(PI*n*p.y) - sin(PI*n*p.x) * sin(PI*m*p.y);\n}\n\nfloat field(vec2 p, float ph){\n  // two mode pairs crossfaded by a periodic weight -> the FIELD loops exactly\n  float w = 0.5 + 0.5 * cos(2.0 * PI * ph);\n  float m = mix(3.0, 5.0, w);\n  float n = mix(5.0, 8.0, w);\n  return chladni(p, m, n);\n}\n\nvoid main(){\n  vec2 uv = isf_FragNormCoord;\n  vec2 p = uv * 2.0 - 1.0;\n  p.x *= RENDERSIZE.x / RENDERSIZE.y;\n\n  float ph = fract(TIME / 9.0);\n  float A = field(p, ph);\n\n  if (PASSINDEX == 0){\n    vec3 prev = IMG_THIS_PIXEL(sand).rgb;\n    float dt = min(TIMEDELTA * 60.0, 2.0);\n\n    float nodal = 1.0 - smoothstep(0.0, 0.05, abs(A));\n    // grains only settle where the plate is actually moving little elsewhere too --\n    // bias deposit by a touch of node sharpness so lines stay thin instead of filling\n    float sharp = 1.0 - smoothstep(0.0, 0.22, abs(A));\n    float deposit = nodal * mix(0.35, 1.0, sharp) * rate * dt;\n\n    vec3 s = prev * (1.0 - 0.02 * dt) + vec3(deposit);\n\n    // soft-knee compress highlights, hard clamp as backstop (float feedback can diverge)\n    vec3 hot = max(s - 1.0, 0.0);\n    s = min(s, 1.0) + hot / (1.0 + hot);\n    s = clamp(s, 0.0, 4.0);\n\n    gl_FragColor = vec4(s, 1.0);\n  } else {\n    vec3 sandCol = IMG_THIS_PIXEL(sand).rgb;\n\n    float shimmer = 0.5 + 0.5 * sin(A * 5.0 - TIME * 0.6);\n    vec3 plate = mix(plateHue.rgb * 0.5, plateHue.rgb * 1.6, shimmer);\n\n    vec3 grains = sandCol * grain.rgb * 1.35;\n    vec3 col = plate + grains;\n\n    col = col / (1.0 + col);\n    col = pow(col, vec3(0.4545));\n\n    gl_FragColor = vec4(col, 1.0);\n  }\n}\n",
    "generator": {
      "model": "claude-sonnet-5",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}