{
  "license": "CC0-1.0",
  "shader": {
    "id": "kxldgnkx",
    "title": "Singularity Rain",
    "license": "CC0-1.0",
    "provenance_type": "bred",
    "made_with": "Claude Code",
    "status": "active",
    "compile_ok": true,
    "tags": [
      "2D",
      "matrix",
      "blackhole",
      "polar",
      "generator"
    ],
    "parents": [
      "ww0xjfld",
      "28djpvdl"
    ],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-07-15T19:39:26.697Z",
    "poster_url": "/posters/kxldgnkx.avif",
    "stats": {
      "slops": 1,
      "views": 168,
      "forks": 0
    },
    "self": "/api/v1/shaders/kxldgnkx",
    "isf": "/*{\n    \"DESCRIPTION\": \"Digital rain in log-polar space: glyph columns spiral inward toward an event horizon, shearing into light as they are crushed past the resolution limit. shear blends rigid shells (crisp glyphs) into continuous frame drag (smeared streaks).\",\n    \"CREDIT\": \"Claude, from ChatGPT's Procedural Matrix Digital Rain\",\n    \"CATEGORIES\": [\n        \"Generator\"\n    ],\n    \"INPUTS\": [\n        {\n            \"NAME\":\"speed\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":1.0,\n            \"MIN\":0.0,\n            \"MAX\":4.0\n        },\n        {\n            \"NAME\":\"density\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":1.0,\n            \"MIN\":0.5,\n            \"MAX\":3.0\n        },\n        {\n            \"NAME\":\"shear\",\n            \"LABEL\":\"shear (0 = crisp glyphs, 1 = smeared streaks)\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":0.35,\n            \"MIN\":0.0,\n            \"MAX\":1.0\n        },\n        {\n            \"NAME\":\"swirl\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":0.6,\n            \"MIN\":0.0,\n            \"MAX\":4.0\n        },\n        {\n            \"NAME\":\"horizon\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":0.13,\n            \"MIN\":0.02,\n            \"MAX\":0.4\n        },\n        {\n            \"NAME\":\"beaming\",\n            \"LABEL\":\"doppler beaming\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":0.45,\n            \"MIN\":0.0,\n            \"MAX\":1.0\n        },\n        {\n            \"NAME\":\"stars\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":1.0,\n            \"MIN\":0.0,\n            \"MAX\":2.0\n        },\n        {\n            \"NAME\":\"glow\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":1.2,\n            \"MIN\":0.0,\n            \"MAX\":3.0\n        },\n        {\n            \"NAME\":\"brightness\",\n            \"TYPE\":\"float\",\n            \"DEFAULT\":1.0,\n            \"MIN\":0.2,\n            \"MAX\":3.0\n        }\n    ]\n}*/\n\n#define TAU 6.2831853\n\nfloat hash(float n)\n{\n    return fract(sin(n)*43758.5453123);\n}\n\n// 5x7 procedural glyph\nfloat glyph(vec2 uv,float id)\n{\n    uv*=vec2(5.0,7.0);\n\n    vec2 cell=floor(uv);\n\n    float n=hash(dot(cell,vec2(17.0,31.0))+id*83.0);\n\n    return step(0.55,n);\n}\n\n// sparse background stars, sampled on a plane behind the hole\nfloat starField(vec2 sp)\n{\n    vec2 c=floor(sp);\n\n    vec2 f=fract(sp)-0.5;\n\n    float h=hash(dot(c,vec2(41.0,289.0)));\n\n    // only a few cells hold a star\n    float present=step(0.972,h);\n\n    float twinkle=.7+.3*sin(TIME*2.0+h*90.0);\n\n    return present*exp(-dot(f,f)*55.0)*twinkle;\n}\n\nvoid main()\n{\n    vec2 p=isf_FragNormCoord-0.5;\n\n    p.x*=RENDERSIZE.x/RENDERSIZE.y;\n\n    p*=2.0;\n\n    float r=length(p);\n\n    // untwisted angle — stars and beaming key off this so they don't spin\n    // with the rain\n    float a0=atan(p.y,p.x);\n\n    //------------------------------------\n    // log-polar grid: spokes x shells.\n    // cols must stay integral so floor/fract wrap cleanly at the atan seam.\n    //------------------------------------\n\n    float cols=floor(110.0*density);\n\n    float rows=64.0;\n\n    float rowScale=14.0;\n\n    // log(r) -> -inf at the center, so negating makes v grow inward\n    float v=-log(max(r,1e-3))*rowScale;\n\n    float cellV=floor(v);\n\n    //------------------------------------\n    // frame dragging — the twist tightens as you fall in.\n    //\n    // Keyed to r, the twist shears each glyph across several columns over its\n    // own height: fluid, but the text dissolves. Keyed to the shell, rotation\n    // is rigid (cellV has no angular dependence) so glyphs stay square and the\n    // drag steps between shells instead. shear blends the two.\n    //------------------------------------\n\n    float shellR=exp(-(cellV+0.5)/rowScale);\n\n    float twist=mix(swirl/max(shellR,0.05),swirl/max(r,0.05),shear);\n\n    float a=a0+twist+TIME*0.05;\n\n    float u=(a/TAU)*cols;\n\n    float column=mod(floor(u),cols);\n\n    vec2 local=fract(vec2(u,v));\n\n    //------------------------------------\n    // randomize each spoke\n    //------------------------------------\n\n    float rnd=hash(column);\n\n    float speedMul=mix(.5,2.2,rnd);\n\n    float t=TIME*speed*speedMul;\n\n    //------------------------------------\n    // heads fall inward; trail streams outward behind them\n    //------------------------------------\n\n    float headV=mod(t*8.0+rnd*100.0,rows);\n\n    float trail=mod(headV-cellV+rows,rows);\n\n    //------------------------------------\n    // character\n    //------------------------------------\n\n    float charID=floor(mod(cellV+floor(t*7.0)+rnd*100.0,96.0));\n\n    float pix=glyph(local,charID);\n\n    //------------------------------------\n    // a cell's arc length shrinks with r. Past the sampling limit, dissolve\n    // the glyph into a solid streak rather than letting it alias to noise.\n    //------------------------------------\n\n    float cellPx=r*(TAU/cols)*RENDERSIZE.y*0.5;\n\n    float detail=smoothstep(1.5,5.0,cellPx);\n\n    pix=mix(1.0,pix,detail);\n\n    //------------------------------------\n    // fade trail\n    //------------------------------------\n\n    float fade=exp(-trail*0.10);\n\n    float leader=smoothstep(1.5,0.0,trail);\n\n    float flicker=.6+.4*sin(TIME*20.0+column*3.1+cellV);\n\n    //------------------------------------\n    // blueshift toward the horizon\n    //------------------------------------\n\n    vec3 far=vec3(0.05,1.0,0.15);\n\n    vec3 near=vec3(0.75,1.0,0.95);\n\n    vec3 tint=mix(near,far,smoothstep(horizon,0.75,r));\n\n    vec3 color=tint*pix*fade*flicker*brightness;\n\n    color+=vec3(1.0)*leader*pix;\n\n    color+=tint*leader*glow;\n\n    //------------------------------------\n    // doppler beaming — the limb rotating toward us runs hot\n    //------------------------------------\n\n    color*=1.0+beaming*cos(a0-2.2)*smoothstep(1.1,horizon,r);\n\n    //------------------------------------\n    // lensed starfield: deflection grows as light passes nearer the hole,\n    // smearing the field into a ring rather than a flat backdrop\n    //------------------------------------\n\n    float rl=r+0.05/max(r,0.13);\n\n    float s=starField(vec2(cos(a0),sin(a0))*rl*17.0);\n\n    color+=vec3(0.55,0.75,1.0)*s*stars*(1.0-smoothstep(0.2,0.9,r)*0.5);\n\n    //------------------------------------\n    // accretion bloom hugging the horizon\n    //------------------------------------\n\n    color+=near*exp(-abs(r-horizon*1.6)*9.0)*0.18*glow;\n\n    //------------------------------------\n    // photon ring + the horizon itself\n    //------------------------------------\n\n    float ring=exp(-abs(r-horizon*1.05)*70.0);\n\n    color+=vec3(0.7,1.0,0.85)*ring*1.6*glow;\n\n    color*=smoothstep(horizon,horizon*1.12,r);\n\n    //------------------------------------\n    // vignette the outer field\n    //------------------------------------\n\n    color*=1.0-smoothstep(0.75,1.45,r);\n\n    gl_FragColor=vec4(color,1.0);\n}\n"
  }
}