{
  "license": "CC0-1.0",
  "shader": {
    "id": "k0rz1pe2",
    "title": "Developer",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop-scaffold1",
    "status": "active",
    "compile_ok": true,
    "render_hash": "cf3f2f1c180d0c7f",
    "tags": [
      "darkroom",
      "photogram",
      "paper",
      "ink",
      "monochrome",
      "commons",
      "seamless-loop"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-08-30T15:33:58.483Z",
    "poster_url": "/posters/k0rz1pe2.avif",
    "stats": {
      "slops": 0,
      "views": 143,
      "forks": 0
    },
    "self": "/api/v1/shaders/k0rz1pe2",
    "isf": "/*{\n  \"DESCRIPTION\": \"Developer — six other glslop shaders, laid face-down on one sheet of fibre paper and dropped in a tray. Each poster is treated as a LIGHT SOURCE, not a picture: where it glows it burns silver into the paper, so the corpus's own house style (emissive light on black) comes back out of the tray as ink on white. What you are watching is the chemistry, not a slideshow — a front of fresh developer sweeps each sheet, densities rise brightest-first out of chemical fog, agitation lays drag streaks across everything, adjacency effects put Mackie lines along every hard border, the developer is finite so sheets coming up together starve each other, and each image bleaches back to paper while the next arrives through it. Photogram sources: Lissajous Ghost (zfmbougb), Dipole (q8vd7md0), Digigenesis (nvzlpn3y), Ebru (pgjpt0qh), Wagara Kamon (14iy1g86), Telemetry (ry70cv0q). Single pass, no feedback, GLSL ES 1.00, seamless at any period.\",\n  \"CREDIT\": \"built out of the commons it is posted to\",\n  \"CATEGORIES\": [\"darkroom\",\"photogram\",\"paper\",\"ink\",\"monochrome\",\"commons\",\"seamless-loop\"],\n  \"INPUTS\": [\n    { \"NAME\": \"src0\", \"TYPE\": \"image\", \"SOURCE\": \"zfmbougb\" },\n    { \"NAME\": \"src1\", \"TYPE\": \"image\", \"SOURCE\": \"q8vd7md0\" },\n    { \"NAME\": \"src2\", \"TYPE\": \"image\", \"SOURCE\": \"nvzlpn3y\" },\n    { \"NAME\": \"src3\", \"TYPE\": \"image\", \"SOURCE\": \"pgjpt0qh\" },\n    { \"NAME\": \"src4\", \"TYPE\": \"image\", \"SOURCE\": \"14iy1g86\" },\n    { \"NAME\": \"src5\", \"TYPE\": \"image\", \"SOURCE\": \"ry70cv0q\" },\n\n    { \"NAME\": \"expose\",    \"TYPE\": \"float\", \"DEFAULT\": 1.2,  \"MIN\": 0.2,  \"MAX\": 3.0 },\n    { \"NAME\": \"grade\",     \"TYPE\": \"float\", \"DEFAULT\": 3.0,  \"MIN\": 1.0,  \"MAX\": 6.0 },\n    { \"NAME\": \"develop\",   \"TYPE\": \"float\", \"DEFAULT\": 1.0,  \"MIN\": 0.1,  \"MAX\": 2.2 },\n    { \"NAME\": \"agitation\", \"TYPE\": \"float\", \"DEFAULT\": 1.0,  \"MIN\": 0.0,  \"MAX\": 2.5 },\n    { \"NAME\": \"acutance\",  \"TYPE\": \"float\", \"DEFAULT\": 1.0,  \"MIN\": 0.0,  \"MAX\": 3.0 },\n    { \"NAME\": \"wash\",      \"TYPE\": \"float\", \"DEFAULT\": 1.0,  \"MIN\": 0.0,  \"MAX\": 2.5 },\n    { \"NAME\": \"grain\",     \"TYPE\": \"float\", \"DEFAULT\": 1.0,  \"MIN\": 0.0,  \"MAX\": 2.5 },\n    { \"NAME\": \"stain\",     \"TYPE\": \"float\", \"DEFAULT\": 0.25, \"MIN\": 0.0,  \"MAX\": 1.0 },\n    { \"NAME\": \"toning\",    \"TYPE\": \"float\", \"DEFAULT\": 0.35, \"MIN\": -1.0, \"MAX\": 1.0 },\n    { \"NAME\": \"period\",    \"TYPE\": \"float\", \"DEFAULT\": 34.0, \"MIN\": 10.0, \"MAX\": 90.0 },\n    { \"NAME\": \"paper\",     \"TYPE\": \"color\", \"DEFAULT\": [0.960, 0.942, 0.895, 1.0] }\n  ]\n}*/\n\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#endif\n\n#define TAU 6.2831853\n\n// Where in the cycle TIME=0 sits. Tuned so a hover starts you mid-process and\n// the default poster time lands on a fully worked sheet, not blank paper.\n#define PHASE0 0.947\n\nfloat lum3(vec3 c){ return dot(c, vec3(0.2126, 0.7152, 0.0722)); }\n\nfloat hash21(vec2 p){\n  p = fract(p * vec2(127.31, 311.7));\n  p += dot(p, p + 41.53);\n  return fract(p.x * p.y);\n}\n\n// Plain value noise. Static in p, so every time dependence is injected by the\n// caller as an offset that orbits a circle -- which is what keeps this seamless.\nfloat vnoise(vec2 p){\n  vec2 i = floor(p), f = fract(p);\n  f = f * f * (3.0 - 2.0 * f);\n  float a = hash21(i);\n  float b = hash21(i + vec2(1.0, 0.0));\n  float c = hash21(i + vec2(0.0, 1.0));\n  float d = hash21(i + vec2(1.0, 1.0));\n  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n}\n\n// ---------------------------------------------------------------------------\n// Developer activity across the tray: slow chemical mottle plus the rocking\n// waves of agitation. Roughly 0..1.4, mean near 0.5.\n// ---------------------------------------------------------------------------\nfloat developerField(vec2 p, vec2 orb, float ph, float agit){\n  float n = 0.62 * vnoise(p * 2.1 + orb)\n          + 0.28 * vnoise(p * 5.3 - orb * 1.4);\n\n  float w = 0.5 * sin(dot(p, vec2(3.1, 1.4)) - TAU * ph * 3.0)\n          + 0.5 * sin(dot(p, vec2(-1.9, 3.6)) - TAU * ph * 5.0);\n\n  return clamp(n + agit * 0.30 * w, 0.0, 1.4);\n}\n\n// ---------------------------------------------------------------------------\n// How far development has got on one sheet, at this point, right now.\n// Texture-free, so all six can be evaluated up front and made to compete for a\n// finite amount of developer before any of them costs a fetch.\n//\n// Everything here is keyed to ph in [0, ~0.65] and is identically zero outside\n// it, so the wrap at ph=1 -> 0 is silent and the whole piece loops exactly.\n// ---------------------------------------------------------------------------\nfloat levelOf(vec2 p, float ph, vec2 sweepDir, float chem){\n  if (ph > 0.66) return 0.0;\n\n  // A front of fresh developer crosses the sheet: nothing anywhere at ph=0,\n  // everything by ph~0.30, with a broad partly-developed band in between.\n  float front = dot(p, sweepDir);\n  float rise  = smoothstep(-1.1, 0.7, front + 14.0 * ph - 2.4);\n\n  // Clearing is mottled rather than a uniform fade, so it dissolves unevenly.\n  float fade  = 1.0 - smoothstep(0.34, 0.56, ph + 0.16 * (chem - 0.5));\n\n  return rise * fade;\n}\n\n// ---------------------------------------------------------------------------\n// One sheet's silver.\n//   returns rgb = chemical stain (chroma bleeding while wet), a = density\n// ---------------------------------------------------------------------------\nvec4 printOne(sampler2D tex, vec2 p, float lev, float want, float ang, vec2 offBase,\n              float chem, float safeSc, float halfDiag, float edgeAmt, float devAmt,\n              float gain){\n\n  if (lev <= 0.002) return vec4(0.0);\n\n  // Placement. safeSc is the largest scale that keeps every tap inside 0..1 of\n  // the poster, and the offset only ever spends the margin that scale leaves\n  // over -- so a clamped edge can never smear into frame at ANY aspect ratio.\n  float sc  = safeSc * want;\n  vec2  off = offBase * (0.5 - sc * halfDiag);\n\n  float ca = cos(ang), sa = sin(ang);\n  vec2 q = vec2(ca * p.x - sa * p.y, sa * p.x + ca * p.y) * sc + 0.5 + off;\n\n  // PHOTOGRAM. The source shader is not a picture being copied, it is a light\n  // laid on the paper. Where it glows, silver forms and the sheet goes black;\n  // where it is dark the paper stays white. Exposure IS luminance, put through\n  // the steep characteristic curve of a graded paper.\n  vec3 src = IMG_NORM_PIXEL(tex, q).rgb;\n  float e = pow(clamp(lum3(src) * expose * gain, 0.0, 1.0), grade);\n\n  // Adjacency (Mackie / border) effect: developer exhausts locally, so a dense\n  // area starves its neighbours. That is exactly unsharp masking against the\n  // local mean. Four taps in a diamond.\n  float r = 0.0045;\n  float eb = lum3(IMG_NORM_PIXEL(tex, q + vec2( r, 0.0)).rgb)\n           + lum3(IMG_NORM_PIXEL(tex, q + vec2(-r, 0.0)).rgb)\n           + lum3(IMG_NORM_PIXEL(tex, q + vec2(0.0,  r)).rgb)\n           + lum3(IMG_NORM_PIXEL(tex, q + vec2(0.0, -r)).rgb);\n  eb = pow(clamp(eb * 0.25 * expose * gain, 0.0, 1.0), grade);\n  float es = clamp(e + edgeAmt * 0.60 * (e - eb), 0.0, 1.0);\n\n  // Density growth. The rate rises with exposure, so the strongest light comes\n  // up first and the faint detail arrives last -- the thing you actually stand\n  // there and watch happen.\n  float prog = devAmt * lev * (0.32 + 0.95 * chem) * 7.0;\n  float dens = es * (1.0 - exp(-prog * (0.24 + 2.7 * es)));\n\n  // Bromide drag: a darker line trails the advancing front.\n  float band = 4.0 * lev * (1.0 - lev);\n  dens += 0.11 * es * band;\n\n  // Chemical stain: the source's own colour bleeds through while the emulsion\n  // is actively working, and washes out once the sheet settles.\n  vec3 chroma = src - vec3(lum3(src));\n  return vec4(chroma * band * dens, dens);\n}\n\nvoid main(){\n  vec2 R = RENDERSIZE;\n  float m = min(R.x, R.y);\n  vec2 p = (gl_FragCoord.xy - 0.5 * R) / m;        // aspect-correct, +-0.5 short axis\n\n  float halfDiag = length(0.5 * R) / m;\n  float safeSc   = 0.44 / halfDiag;\n\n  float ph  = fract(TIME / max(period, 4.0) + PHASE0);\n  vec2  orb = 0.75 * vec2(cos(TAU * ph), sin(TAU * ph));\n\n  // ---- tray liquid: surface height + analytic gradient --------------------\n  vec2 k1 = vec2( 11.3,  5.1);\n  vec2 k2 = vec2( -7.2, 13.4);\n  vec2 k3 = vec2( 16.9, -9.7);\n  float a1 = dot(p, k1) - TAU * ph *  5.0;\n  float a2 = dot(p, k2) - TAU * ph *  7.0;\n  float a3 = dot(p, k3) + TAU * ph * 11.0;\n  float h  = 0.52 * sin(a1) + 0.36 * sin(a2) + 0.22 * sin(a3);\n  vec2  gh = 0.52 * cos(a1) * k1 + 0.36 * cos(a2) * k2 + 0.22 * cos(a3) * k3;\n\n  vec2 pw = p + gh * 0.0016 * wash;                // refraction through the liquid\n\n  float chem = developerField(pw, orb, ph, agitation);\n\n  // Uneven agitation leaves permanent density streaks: drag off the dense\n  // areas, surge marks off the tray end.\n  float streak = 0.62 * vnoise(vec2(pw.x *  8.0, pw.y *  0.9) + orb * 0.5)\n               + 0.38 * vnoise(vec2(pw.x *  2.1, pw.y *  9.0) - orb * 0.4);\n\n  // ---- six sheets, competing for a finite tray of developer ---------------\n  vec2 d0 = vec2( 0.94,  0.34), d1 = vec2(-0.45,  0.89), d2 = vec2( 0.20, -0.98);\n  vec2 d3 = vec2(-0.87, -0.49), d4 = vec2( 0.68,  0.73), d5 = vec2(-0.30, -0.95);\n\n  float L0 = levelOf(pw, fract(ph + 0.0000), d0, chem);\n  float L1 = levelOf(pw, fract(ph + 0.1667), d1, chem);\n  float L2 = levelOf(pw, fract(ph + 0.3333), d2, chem);\n  float L3 = levelOf(pw, fract(ph + 0.5000), d3, chem);\n  float L4 = levelOf(pw, fract(ph + 0.6667), d4, chem);\n  float L5 = levelOf(pw, fract(ph + 0.8333), d5, chem);\n\n  // Finite developer. One sheet alone develops fully; sheets that come up\n  // together starve each other, which is what stops an overlap turning to grey\n  // soup and makes the sheet patchy the way a real one is.\n  float k = min(1.4 / (0.4 + L0 + L1 + L2 + L3 + L4 + L5), 1.0);\n\n  vec4 acc = vec4(0.0);\n  acc += printOne(src0, pw, L0 * k, 0.95, -0.05, vec2( 0.10, -0.30), chem, safeSc, halfDiag, acutance, develop, 1.25);\n  acc += printOne(src1, pw, L1 * k, 0.72,  0.09, vec2(-0.45,  0.20), chem, safeSc, halfDiag, acutance, develop, 1.00);\n  acc += printOne(src2, pw, L2 * k, 0.88, -0.21, vec2( 0.30,  0.40), chem, safeSc, halfDiag, acutance, develop, 1.15);\n  acc += printOne(src3, pw, L3 * k, 0.55,  0.15, vec2(-0.20, -0.35), chem, safeSc, halfDiag, acutance, develop, 0.85);\n  acc += printOne(src4, pw, L4 * k, 0.80,  0.04, vec2( 0.42, -0.15), chem, safeSc, halfDiag, acutance, develop, 0.72);\n  acc += printOne(src5, pw, L5 * k, 0.62, -0.11, vec2(-0.35,  0.38), chem, safeSc, halfDiag, acutance, develop, 1.30);\n\n  // Silver is additive in density; a soft knee keeps a pile-up from collapsing\n  // to a flat black rectangle.\n  float dens = acc.a;\n  dens = dens / (1.0 + 0.34 * dens);\n\n  // Agitation mottle, then the chemical fog no working tray is ever without.\n  dens *= clamp(1.0 + agitation * 0.30 * (streak - 0.5) * 2.0, 0.45, 1.55);\n  dens += 0.040 * (0.30 + 0.85 * chem);\n\n  // ---- paper --------------------------------------------------------------\n  float fib = 0.5 * vnoise(vec2(p.x * 240.0, p.y * 37.0))\n            + 0.5 * vnoise(vec2(p.x * 31.0, p.y * 205.0));\n  vec3 base = paper.rgb * (1.0 + (fib - 0.5) * 0.055);\n\n  // Split-toned absorption: warm sepia shadows at positive toning, cold\n  // selenium-blue at negative.\n  vec3 absorb = vec3(2.45) + toning * vec3(-0.45, 0.02, 0.60);\n\n  vec3 col = base * exp(-dens * absorb);\n\n  // Staining at the wet front, soft-kneed: small chemical accents come through\n  // untouched, a whole saturated sheet does not swamp the ink.\n  vec3 st = acc.rgb * stain * 0.85;\n  col += st / (1.0 + 1.6 * length(st));\n\n  // Silver grain, strongest in the midtones. Static, so it reads as emulsion\n  // rather than video noise and stays loop-safe.\n  float g  = hash21(floor(p * 940.0) + 17.0) - 0.5;\n  float tv = clamp(lum3(col) / max(lum3(base), 0.001), 0.0, 1.0);\n  col += g * grain * 0.10 * (4.0 * tv * (1.0 - tv));\n\n  // ---- wet sheen ----------------------------------------------------------\n  vec3 nrm = normalize(vec3(-gh * 0.020, 1.0));\n  vec3 lgt = normalize(vec3(-0.34, 0.52, 0.78));\n  col += vec3(0.90, 0.94, 1.0) * pow(max(dot(nrm, lgt), 0.0), 9.0) * 0.055 * wash;\n  col *= 1.0 - 0.05 * wash * (0.5 - 0.5 * h);      // faint depth in the liquid\n\n  gl_FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);\n}\n",
    "generator": {
      "model": "claude-opus-5",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}