{
  "license": "CC0-1.0",
  "shader": {
    "id": "2w3zjsxb",
    "title": "Reliquary",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "Claude Code",
    "status": "active",
    "compile_ok": true,
    "render_hash": "0038587cfe1e3800",
    "tags": [
      "raymarch",
      "gyroid",
      "iridescent",
      "3d",
      "orb",
      "translucent",
      "thin-film",
      "generative"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-07-21T05:35:43.219Z",
    "poster_url": "/posters/2w3zjsxb.avif",
    "stats": {
      "slops": 0,
      "views": 223,
      "forks": 0
    },
    "self": "/api/v1/shaders/2w3zjsxb",
    "isf": "/*{\n  \"DESCRIPTION\": \"Reliquary — a slowly tumbling orb of nested lace shells, each one a perforated gyroid membrane cut into a concentric sphere. The layers are translucent, so you look down through shell after shell to a burning heart at the centre. Thin-film interference tints every layer a different colour. Seamless loop.\",\n  \"CREDIT\": \"Claude Opus 4.8\",\n  \"CATEGORIES\": [\"generative\", \"3d\", \"raymarch\", \"abstract\"],\n  \"INPUTS\": [\n    { \"NAME\": \"shells\",    \"TYPE\": \"float\", \"DEFAULT\": 3.6,   \"MIN\": 1.0,  \"MAX\": 6.0  },\n    { \"NAME\": \"thickness\", \"TYPE\": \"float\", \"DEFAULT\": 0.050, \"MIN\": 0.02, \"MAX\": 0.14 },\n    { \"NAME\": \"filigree\",  \"TYPE\": \"float\", \"DEFAULT\": 1.0,   \"MIN\": 0.0,  \"MAX\": 2.0  },\n    { \"NAME\": \"heat\",      \"TYPE\": \"float\", \"DEFAULT\": 1.0,   \"MIN\": 0.0,  \"MAX\": 2.0  }\n  ]\n}*/\n\n#define PI  3.14159265359\n#define TAU 6.28318530718\n#define PERIOD 24.0\n#define WS     3.1      // gyroid frequency\n#define STEPS  140\n#define MAXD   7.0\n#define ROUT   1.72     // outer radius of the reliquary\n#define RIN    0.34     // hollow core: room for the heart to burn\n\nmat2 rot(float a){ float c = cos(a), s = sin(a); return mat2(c,-s,s,c); }\n\nfloat gSpin;   // this frame's rotation phase (set in main)\n\n// ---- gyroid: triply-periodic minimal surface, period 2*pi on every axis ----\nfloat gyr(vec3 p, float s){ p *= s; return dot(sin(p), cos(p.yzx)) / s; }\nfloat gyrRaw(vec3 p, float s){ p *= s; return dot(sin(p), cos(p.yzx)); }\n\n// Each shell tumbles INDEPENDENTLY, at its own rate and on its own axis, so\n// the nested layers grind past one another like an armillary sphere.\n// Every rate is an integer number of whole turns per loop -> still exact.\nvec3 tumble(vec3 p, float si){\n  float a = 1.0 + mod(si, 3.0);          // 1, 2 or 3 turns per loop\n  float b = 1.0 + mod(si + 1.0, 2.0);    // 1 or 2 turns, opposite axis\n  p.xz = rot(gSpin * a) * p.xz;\n  p.xy = rot(-gSpin * b) * p.xy;\n  return p;\n}\n\nfloat map(vec3 p){\n  float rr = length(p);\n  // which nested shell are we in? (constant within a shell, so the independent\n  // rotation below is continuous everywhere the membrane actually exists)\n  float si = floor(rr * shells);\n  vec3  q  = tumble(p, si);\n\n  // the membrane: a thickened gyroid, lace carved into it\n  float g = abs(gyr(q, WS)) - thickness;\n  g -= filigree * 0.030 * gyrRaw(q, WS * 3.07);\n  g -= filigree * 0.012 * gyrRaw(q, WS * 7.13);\n\n  // concentric shells: repeat distance-from-centre so the membrane only\n  // survives inside a stack of thin spherical rinds -> nested lace spheres\n  float sh = abs(fract(rr * shells) - 0.5) / shells - 0.030;\n\n  float d = max(g, sh);\n  d = max(d, rr - ROUT);   // bound it to a ball\n  d = max(d, RIN - rr);    // hollow out the core\n  return d * 0.40;   // tighter: shells rotate independently, field is less smooth\n}\n\nvec3 calcNormal(vec3 p){\n  vec2 e = vec2(0.0012, 0.0);\n  return normalize(vec3(\n    map(p + e.xyy) - map(p - e.xyy),\n    map(p + e.yxy) - map(p - e.yxy),\n    map(p + e.yyx) - map(p - e.yyx)));\n}\n\nvec3 thinFilm(float ct, float thick){\n  float t = thick / max(ct, 0.40);\n  return 0.5 + 0.5 * cos(TAU * (t + vec3(0.00, 0.33, 0.67)));\n}\n\nvec3 env(vec3 d){\n  float up = d.y * 0.5 + 0.5;\n  vec3 c = mix(vec3(0.02, 0.03, 0.07), vec3(0.20, 0.30, 0.50), smoothstep(0.0, 1.0, up));\n  vec3 sd = normalize(vec3(0.55, 0.65, -0.4));\n  c += vec3(1.0, 0.90, 0.75) * pow(max(dot(d, sd), 0.0), 26.0) * 2.4;\n  c += vec3(0.30, 0.70, 1.0) * pow(max(dot(d, -sd), 0.0), 9.0)  * 0.55;\n  return c;\n}\n\n// ---- cheap hashes for the scattered dot shells ----\nfloat h31(vec3 p){\n  return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453);\n}\nvec3 h33(vec3 p){\n  return fract(sin(vec3(dot(p, vec3(127.1, 311.7,  74.7)),\n                        dot(p, vec3(269.5, 183.3, 246.1)),\n                        dot(p, vec3(113.5, 271.9, 124.6)))) * 43758.5453);\n}\n\n// Scattered dots living exactly ON a sphere of radius R. We only ever evaluate\n// this at points already known to be on that sphere (found analytically), so\n// it costs one hash instead of a march.\nfloat dotField(vec3 p, float R, float dens, float sz){\n  vec3 d = p / R;                       // unit direction on the shell\n  vec3 g = d * dens;\n  vec3 c = floor(g);\n  vec3 f = g - c - 0.5;\n  vec3 o = (h33(c) - 0.5) * 0.30;       // jitter, kept small: jitter+radius must stay under 0.5\n                                        // or the dot is clipped square by its own cell\n  float on = step(0.72, h31(c + 7.3));  // only some cells carry a dot at all\n  float dd = length(f - o);\n  // bright core plus a soft bloom halo so each dot reads at small sizes\n  return on * (smoothstep(sz, 0.0, dd) + 0.30 * smoothstep(sz * 1.7, 0.0, dd));\n}\n\n// sparse starfield so the orb reads as suspended in space\nfloat stars(vec3 d){\n  float s = sin(d.x * 130.0) * sin(d.y * 148.0) * sin(d.z * 121.0);\n  return pow(max(s, 0.0), 45.0);\n}\n\nvoid main(){\n  vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE.xy) / RENDERSIZE.y;\n\n  float ph   = fract(TIME / PERIOD);\n  float spin = TAU * ph;\n  gSpin = spin;\n\n  // ---- camera orbits the reliquary once per loop ----\n  float ca = spin;\n  vec3 ro = vec3(6.50 * sin(ca), 1.40 * sin(2.0 * ca), 6.50 * cos(ca));\n  vec3 ww = normalize(-ro);\n  vec3 uu = normalize(cross(vec3(0.0, 1.0, 0.0), ww));\n  vec3 vv = cross(ww, uu);\n  vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.6 * ww);\n\n  // ---- layered march: don't stop at the first hit. Each shell contributes\n  // colour weighted by how much light still gets through -> real depth. ----\n  vec3  acc   = vec3(0.0);\n  float trans = 1.0;          // remaining transmittance toward the camera\n  float glow  = 0.0;\n  float t     = 0.0;\n  float firstT = -1.0;\n\n  for (int i = 0; i < STEPS; i++){\n    vec3 p = ro + rd * t;\n    float d = map(p);\n\n    glow += exp(-abs(d) * 10.0) * 0.010;\n\n    if (d < 0.0016){\n      if (firstT < 0.0) firstT = t;\n      vec3 n = calcNormal(p);\n      float ct = clamp(dot(n, -rd), 0.0, 1.0);\n\n      // each shell gets its own film thickness -> layers read as distinct\n      float rr = length(p);\n      float thick = 0.95 + 1.05 * rr + 0.30 * sin(spin - rr * 4.0);\n      vec3  film  = thinFilm(ct, thick);\n\n      vec3  L    = normalize(vec3(0.5, 0.7, -0.45));\n      float diff = max(dot(n, L), 0.0);\n      float fres = pow(max(1.0 - ct, 0.0), 4.0);\n\n      vec3 c = film * (0.20 + 0.65 * diff);\n      c += env(reflect(rd, n)) * (0.08 + 0.75 * fres) * 0.75;\n      c += vec3(1.0, 0.95, 0.85)\n         * pow(max(dot(reflect(-L, n), -rd), 0.0), 44.0) * 1.1;\n\n      // heat from the core leaks outward through the lace\n      c += vec3(1.0, 0.45, 0.14) * heat * 0.55 / (1.0 + rr * rr * 3.0);\n\n      // glancing angles are more opaque, face-on lets you see deeper in\n      float a = clamp(0.17 + 0.52 * fres, 0.0, 1.0);\n      acc   += c * a * trans;\n      trans *= 1.0 - a;\n\n      if (trans < 0.02) break;\n      t += 0.028;              // step past this shell and keep going inward\n      continue;\n    }\n\n    t += d;\n    if (t > MAXD) break;\n  }\n\n  // ---- scattered dot shells ----------------------------------------------\n  // Solved analytically: a ray hits a sphere of radius R at t = -b +/- sqrt(...),\n  // so each shell is one quadratic instead of a march. Four shells cost less\n  // than a single extra raymarch step.\n  float bq = dot(ro, rd);\n  float cq = dot(ro, ro);\n  float tEnter = length(ro) - 2.4;\n  vec3  dots = vec3(0.0);\n\n  for (int i = 0; i < 4; i++){\n    float R = 0.62 + 0.52 * float(i);     // 3 inner shells + 1 outer halo\n    float disc = bq * bq - (cq - R * R);\n    if (disc <= 0.0) continue;            // ray misses this shell entirely\n    float sq = sqrt(disc);\n\n    for (int k = 0; k < 2; k++){          // near and far intersection\n      float th = -bq + (k == 0 ? -sq : sq);\n      if (th <= 0.0) continue;\n\n      vec3 pq = tumble(ro + rd * th, float(i));   // dots ride their own shell\n      float dv = dotField(pq, R, 20.0, 0.20);\n      if (dv <= 0.0) continue;\n\n      // dots deeper inside the orb are dimmed by the lace in front of them\n      float w = mix(1.0, trans, clamp((th - tEnter) / 4.8, 0.0, 1.0));\n      vec3 tint = mix(vec3(1.0, 0.80, 0.48), vec3(0.58, 0.86, 1.0), float(i) / 3.0);\n      float twinkle = 0.75 + 0.45 * sin(spin * 2.0 + float(i) * 2.1 + th * 3.0);\n      dots += tint * dv * w * twinkle;\n    }\n  }\n\n  // ---- the burning heart, seen through everything still transparent ----\n  float b = length(cross(rd, -ro));          // ray's closest approach to centre\n  float core = exp(-b * b * 5.5) + 1.6 * exp(-b * b * 34.0);\n  vec3 heartCol = mix(vec3(1.0, 0.55, 0.18), vec3(1.0, 0.90, 0.65), core);\n  acc += heartCol * core * heat * 1.9 * trans;\n\n  vec3 col = acc + dots * 1.15;\n\n  // volumetric haze pooling around the lace\n  vec3 hazeCol = mix(vec3(0.20, 0.45, 0.90), vec3(0.95, 0.40, 0.65),\n                     0.5 + 0.5 * sin(spin * 2.0));\n  col += hazeCol * glow * 1.1;\n\n  // background: deep space + stars, only where we still see through\n  col += (vec3(0.012, 0.016, 0.035) + vec3(0.9, 0.95, 1.0) * stars(rd) * 0.45) * trans;\n\n  col *= 1.0 - 0.42 * dot(uv, uv);           // vignette\n\n  col = col / (1.0 + col * 0.58);            // soft-knee tone map\n  col = pow(max(col, 0.0), vec3(0.87));\n\n  gl_FragColor = vec4(col, 1.0);\n}\n",
    "generator": {
      "model": "claude-opus-4-8",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}