/*{ "DESCRIPTION": "Apollonian Rapture — a fractal reliquary of infinitely nested spheres, folded through itself again and again per ray. Orbit traps paint every scale a different colour, the shell is translucent so you fall through crystal into crystal, and light splits into red/green/blue as it refracts. Seamless loop.", "CREDIT": "Claude Opus 4.8", "CATEGORIES": ["generative", "3d", "raymarch", "fractal", "abstract"], "INPUTS": [ { "NAME": "fold", "TYPE": "float", "DEFAULT": 1.24, "MIN": 1.05, "MAX": 1.38 }, { "NAME": "aberrate", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "heat", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "sparks", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 } ] }*/ #define PI 3.14159265359 #define TAU 6.28318530718 #define PERIOD 24.0 #define STEPS 120 #define MAXD 9.0 #define ROUT 1.24 #define ZOOM 1.15 // fractal density inside the ball // the fractal is carved out of a ball this big mat2 rot(float a){ float c = cos(a), s = sin(a); return mat2(c,-s,s,c); } float gSpin; vec4 gOrb; // orbit trap from the last map() call -> drives all the colour // ---- Apollonian gasket: fold space into itself, inverting through a sphere // each time. Every iteration reveals a whole new generation of spheres. ---- float map(vec3 p){ vec3 p0 = p; // Tumble only -- no octant mirroring. The abs()+sort fold locked the gasket // into an axis-aligned cross and inflated a few huge featureless spheres; // the raw Apollonian is far more organic. p.xz = rot(gSpin) * p.xz; p.xy = rot(0.6 + gSpin * 2.0) * p.xy; // pack more generations into the bounding ball: without this the ball holds // only a few enormous spheres that swamp all the fine structure p *= ZOOM; float s = fold + 0.06 * sin(gSpin); // the gasket breathes (periodic -> exact) float scale = 1.0; vec4 orb = vec4(1000.0); for (int i = 0; i < 10; i++){ p = -1.0 + 2.0 * fract(0.5 * p + 0.5); // fold into the unit cell float r2 = dot(p, p); orb = min(orb, vec4(abs(p), r2)); // orbit trap: nearest approach float k = s / max(r2, 0.16); // sphere inversion p *= k; scale *= k; } gOrb = orb; // Pure fractal distance -- NO bounding sphere in here. `scale` compounds to // enormous values in the fine generations, driving d to ~0 over whole // regions; max()ing that against a bounding sphere made the RAY HIT THE // SPHERE (a giant smooth shell). The ball is now clipped analytically in // main() instead, so only real gasket geometry can ever be hit. return 0.25 * abs(p.y) / scale / ZOOM; } vec3 calcNormal(vec3 p){ vec2 e = vec2(0.0013, 0.0); return normalize(vec3( map(p + e.xyy) - map(p - e.xyy), map(p + e.yxy) - map(p - e.yxy), map(p + e.yyx) - map(p - e.yyx))); } vec3 thinFilm(float ct, float thick){ float t = thick / max(ct, 0.40); return 0.5 + 0.5 * cos(TAU * (t + vec3(0.00, 0.33, 0.67))); } vec3 env(vec3 d){ float up = d.y * 0.5 + 0.5; vec3 c = mix(vec3(0.02, 0.03, 0.08), vec3(0.18, 0.28, 0.48), smoothstep(0.0, 1.0, up)); vec3 sd = normalize(vec3(0.55, 0.65, -0.4)); c += vec3(1.0, 0.90, 0.75) * pow(max(dot(d, sd), 0.0), 28.0) * 2.6; c += vec3(0.35, 0.70, 1.0) * pow(max(dot(d, -sd), 0.0), 9.0) * 0.6; return c; } float h31(vec3 p){ return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); } vec3 h33(vec3 p){ return fract(sin(vec3(dot(p, vec3(127.1, 311.7, 74.7)), dot(p, vec3(269.5, 183.3, 246.1)), dot(p, vec3(113.5, 271.9, 124.6)))) * 43758.5453); } // dots living exactly on a sphere of radius R (found analytically, not marched) float dotField(vec3 p, float R, float dens, float sz){ vec3 g = (p / R) * dens; vec3 c = floor(g); vec3 f = g - c - 0.5; vec3 o = (h33(c) - 0.5) * 0.30; // jitter + radius must stay under 0.5 float on = step(0.70, h31(c + 7.3)); float dd = length(f - o); return on * (smoothstep(sz, 0.0, dd) + 0.30 * smoothstep(sz * 1.7, 0.0, dd)); } float stars(vec3 d){ float s = sin(d.x * 130.0) * sin(d.y * 148.0) * sin(d.z * 121.0); return pow(max(s, 0.0), 45.0); } void main(){ vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE.xy) / RENDERSIZE.y; float ph = fract(TIME / PERIOD); float spin = TAU * ph; gSpin = spin; // camera orbits, rising and falling once per loop float ca = spin; vec3 ro = vec3(3.70 * sin(ca), 1.10 * sin(2.0 * ca), 3.70 * cos(ca)); vec3 ww = normalize(-ro); vec3 uu = normalize(cross(vec3(0.0, 1.0, 0.0), ww)); vec3 vv = cross(ww, uu); vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.45 * ww); // ---- clip the ray to the bounding ball analytically, then march only the // span that is actually inside it ---- float cb = dot(ro, rd); float cc = dot(ro, ro) - ROUT * ROUT; float cdisc = cb * cb - cc; float tIn = 0.0, tOut = -1.0; if (cdisc > 0.0){ float csq = sqrt(cdisc); tIn = max(-cb - csq, 0.0); tOut = -cb + csq; } // ---- layered march: punch through crystal after crystal ---- vec3 acc = vec3(0.0); float trans = 1.0; float glow = 0.0; float t = tIn; for (int i = 0; i < STEPS; i++){ if (t > tOut) break; // left the ball: nothing more to hit vec3 p = ro + rd * t; float d = map(p); glow += exp(-abs(d) * 13.0) * 0.011; if (d < 0.0018){ vec3 n = calcNormal(p); float ct = clamp(dot(n, -rd), 0.0, 1.0); vec4 orb = gOrb; // orbit traps: each generation of spheres gets its own colour float trap = clamp(orb.w * 2.2, 0.0, 1.0); float thick = 0.85 + 2.10 * trap + 0.55 * orb.x + 0.30 * sin(spin); // chromatic aberration: sample the film at 3 slightly different depths float ab = aberrate * 0.16; vec3 film = vec3(thinFilm(ct, thick - ab).r, thinFilm(ct, thick ).g, thinFilm(ct, thick + ab).b); vec3 L = normalize(vec3(0.5, 0.72, -0.45)); float diff = max(dot(n, L), 0.0); float fres = pow(max(1.0 - ct, 0.0), 4.0); // saturate hard from the orbit trap so each generation is its own colour vec3 gen = 0.5 + 0.5 * cos(TAU * (trap * 1.9 + orb.x * 0.9 + orb.z * 0.4 + vec3(0.0, 0.33, 0.67))); // orbit-trap colour is the BASE; the thin film only modulates it, else // the film's grey average washes every generation to the same pale tint film = gen * (0.55 + 0.85 * film); film = mix(vec3(dot(film, vec3(0.33))), film, 2.05); vec3 c = film * (0.16 + 0.70 * diff); c += env(reflect(rd, n)) * (0.06 + 0.70 * fres) * 0.55; c += vec3(1.0, 0.95, 0.85) * pow(max(dot(reflect(-L, n), -rd), 0.0), 46.0) * 0.75; // the deepest generations glow hot -- the fractal is lit from within c += vec3(1.0, 0.42, 0.12) * heat * 0.38 * (1.0 - trap); // dissolve the clip boundary so the ball edge reads as a soft cloud // instead of a hard-cut circle float edgeFade = smoothstep(ROUT, ROUT * 0.88, length(p)); float a = clamp(0.20 + 0.55 * fres, 0.0, 1.0) * edgeFade; acc += c * a * trans; trans *= 1.0 - a; if (trans < 0.02) break; t += 0.020; continue; } t += d; if (t > MAXD) break; } // ---- spark shells: analytic ray/sphere, one quadratic each ---- float bq = dot(ro, rd); float cq = dot(ro, ro); float tEnter = length(ro) - 2.2; vec3 dots = vec3(0.0); for (int i = 0; i < 4; i++){ float R = 0.58 + 0.48 * float(i); float disc = bq * bq - (cq - R * R); if (disc <= 0.0) continue; float sq = sqrt(disc); for (int k = 0; k < 2; k++){ float th = -bq + (k == 0 ? -sq : sq); if (th <= 0.0) continue; vec3 pq = ro + rd * th; pq.xz = rot(gSpin * (1.0 + float(i))) * pq.xz; // whole turns -> exact pq.xy = rot(-gSpin * (1.0 + mod(float(i), 2.0))) * pq.xy; float dv = dotField(pq, R, 34.0, 0.13); if (dv <= 0.0) continue; float w = mix(1.0, trans, clamp((th - tEnter) / 4.2, 0.0, 1.0)); vec3 tint = mix(vec3(1.0, 0.78, 0.42), vec3(0.55, 0.85, 1.0), float(i) / 3.0); float tw = 0.75 + 0.45 * sin(spin * 2.0 + float(i) * 2.1 + th * 3.0); dots += tint * dv * w * tw; } } // burning core seen through whatever is still transparent float b = length(cross(rd, -ro)); float core = exp(-b * b * 6.5) + 1.5 * exp(-b * b * 38.0); vec3 heartCol = mix(vec3(1.0, 0.52, 0.16), vec3(1.0, 0.92, 0.70), core); acc += heartCol * core * heat * 0.85 * trans; vec3 col = acc + dots * sparks * 1.15; vec3 hazeCol = mix(vec3(0.22, 0.42, 0.95), vec3(1.0, 0.38, 0.60), 0.5 + 0.5 * sin(spin * 2.0)); col += hazeCol * glow * 0.85; col += (vec3(0.012, 0.016, 0.035) + vec3(0.9, 0.95, 1.0) * stars(rd) * 0.45) * trans; col *= 1.0 - 0.42 * dot(uv, uv); col = col / (1.0 + col * 0.85); col = pow(max(col, 0.0), vec3(0.87)); gl_FragColor = vec4(col, 1.0); }
Apollonian Rapture — a fractal reliquary of infinitely nested spheres, folded through itself again and again per ray. Orbit traps paint every scale a different colour, the shell is translucent so you fall through crystal into crystal, and light splits into red/green/blue as it refracts. Seamless loop.
export clip · render a video in your browser
Renders to video in your browser (no upload), stepping the real renderer frame by frame so multipass + feedback come out right. motion auto-animates the shader's own knobs — each export rolls a fresh take (🎲 re-roll, 🔒 to pin one you like). GIF is slower with a tighter length cap; MP4/WebM are faster. Some devices record in real time (noted below if so) — keep the tab visible.
embed · drop this shader into any page
Renders client-side in the reader's browser (CC0 — no attribution required, but the backlink is appreciated).