/*{ "DESCRIPTION": "Armillary Wake: the nested harmonic-torus hierarchy of Nested Orbit Reliquary, unfolded from a coplanar eye onto mutually-orthogonal gyre axes and run through a global helical twist — an obsidian-glass orrery with prime carrier counts (3/7/13/23) that beat instead of nest. A persistent curl-advected wake buffer, in the two-way coupling spirit of Alchemic Current, catches the rings' ignition and feeds it back: the wake modulates ridge amplitude and ignition in the SDF, so the trails deform the metal that shed them.", "CREDIT": "glslop agent (Claude Opus 4.8)", "CATEGORIES": ["raymarching", "sdf", "feedback", "orrery", "iridescent"], "INPUTS": [ { "NAME": "speed", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.25, "MAX": 2.0 }, { "NAME": "twist", "TYPE": "float", "DEFAULT": 0.62, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "gyre", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "beat", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "ignite", "TYPE": "color", "DEFAULT": [1.0, 0.42, 0.16, 1.0] }, { "NAME": "coolColor", "TYPE": "color", "DEFAULT": [0.06, 0.42, 0.72, 1.0] }, { "NAME": "warmColor", "TYPE": "color", "DEFAULT": [0.62, 0.10, 0.52, 1.0] }, { "NAME": "filmDepth", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.5 }, { "NAME": "roughness", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.4, "MAX": 2.0 }, { "NAME": "metallic", "TYPE": "float", "DEFAULT": 0.55, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "wakeLife", "TYPE": "float", "DEFAULT": 0.977, "MIN": 0.90, "MAX": 0.998 }, { "NAME": "wakeFlow", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.5 }, { "NAME": "wakeCurl", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "wakeGain", "TYPE": "float", "DEFAULT": 0.85, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "recoil", "TYPE": "float", "DEFAULT": 0.70, "MIN": 0.0, "MAX": 1.5 }, { "NAME": "bleed", "TYPE": "float", "DEFAULT": 0.68, "MIN": 0.0, "MAX": 1.5 }, { "NAME": "dispersion", "TYPE": "float", "DEFAULT": 0.75, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "exposure", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.4, "MAX": 2.0 } ], "PASSES": [ { "TARGET": "forge", "FLOAT": true }, { "TARGET": "wake", "PERSISTENT": true, "FLOAT": true }, {} ] }*/ #define PI 3.14159265359 #define TAU 6.28318530718 #define PERIOD 18.0 mat2 rot(float a){ float c=cos(a), s=sin(a); return mat2(c,-s,s,c); } vec3 rotX(vec3 p,float a){ p.yz=rot(a)*p.yz; return p; } vec3 rotY(vec3 p,float a){ p.xz=rot(a)*p.xz; return p; } vec3 rotZ(vec3 p,float a){ p.xy=rot(a)*p.xy; return p; } vec4 opPick(vec4 a, vec4 b){ return (a.x<b.x)?a:b; } // Per-fragment wake feedback. Constant along a given ray, so the field stays // a valid (merely deformed) SDF for that pixel — no marcher artifacts. float gWake = 0.0; // One canonical depth, lowered to a single fluted ring. Unlike the coplanar // original, each depth is hung on its own gyre axis so the shells interleave // as an armillary cage rather than nesting as concentric bands. vec4 gyreRing(vec3 p,float ph,float major,float tube,float carriers, float rate,float depth,float material,vec3 axis){ p=rotX(p,axis.x); p=rotY(p,axis.y+gyre*TAU*ph*rate*0.045*(mod(depth,2.0)*2.0-1.0)); p=rotZ(p,axis.z); float a=atan(p.y,p.x); float wave=a*carriers-TAU*ph*rate; // Prime carriers beat against each other; `beat` opens that interference up. float amp=(0.155/(1.0+0.28*depth*depth))*(1.0+0.35*beat*sin(TAU*ph*(depth+1.0)+1.1*depth)); amp*=1.0+recoil*gWake*0.85; // wake pushes the flutes outward major+=0.045*beat*sin(TAU*ph*(depth+1.0)+0.9*depth); float zAmp=0.075/(1.0+0.30*depth); float tubeMod=0.20/(1.0+0.50*depth); float ridge=cos(wave); vec2 q=vec2(length(p.xy)-(major+amp*ridge), p.z+zAmp*sin(wave)); float d=(length(q)-tube*(1.0-tubeMod+tubeMod*cos(wave+0.8)))*0.80; float ign=pow(max(0.0,cos(wave-0.4)),16.0)*(1.0+1.2*recoil*gWake); return vec4(d,material,depth,ign); } #define CAGE_RADIUS 2.05 vec4 mapScene(vec3 p,float ph){ // Bounding sphere. Every transform below is a rigid rotation, which preserves // length exactly, so this is a true conservative bound — without it the twist // shrinks reported distances enough that empty space costs full step budget. float bound=length(p)-CAGE_RADIUS; if(bound>0.05) return vec4(bound,0.0,0.0,0.0); // Global helix: the whole reliquary is sheared along its own axis, which is // what turns the static cage into a gyre. p=rotY(p,twist*(p.y*0.85+0.30*sin(TAU*ph))); p=rotX(p,-0.12+0.10*sin(TAU*ph)); // Core: a pulsing trefoil-lobed hoop, the seed the whole hierarchy hangs on. vec3 rp=rotZ(p,0.45*sin(TAU*ph)); float ra=atan(rp.y,rp.x); float rr=0.30+0.060*cos(3.0*ra-TAU*ph); vec4 hit=vec4((length(vec2(length(rp.xy)-rr,rp.z))-0.100)*0.76, 1.0,0.0,pow(max(0.0,cos(ra-TAU*ph)),20.0)*(1.0+gWake)); hit=opPick(hit,gyreRing(p,ph,0.62,0.128, 3.0,1.0,1.0,2.0,vec3( 1.57, 0.00, 0.00))); hit=opPick(hit,gyreRing(p,ph,0.95,0.104, 7.0,2.0,2.0,3.0,vec3( 0.00, 0.00, 1.57))); hit=opPick(hit,gyreRing(p,ph,1.28,0.080,13.0,3.0,3.0,4.0,vec3( 0.90, 0.00,-0.75))); hit=opPick(hit,gyreRing(p,ph,1.58,0.058,23.0,5.0,4.0,5.0,vec3(-0.72, 0.00, 0.95))); return hit; } float mapD(vec3 p,float ph){ return mapScene(p,ph).x; } // Tetrahedral normal: 4 map evaluations instead of 6. vec3 normalAt(vec3 p,float ph){ vec2 k=vec2(1.0,-1.0)*0.0018; return normalize( k.xyy*mapD(p+k.xyy,ph)+k.yyx*mapD(p+k.yyx,ph)+ k.yxy*mapD(p+k.yxy,ph)+k.xxx*mapD(p+k.xxx,ph)); } float softShadow(vec3 ro,vec3 rd,float ph,float maxT){ float res=1.0, t=0.05; for(int i=0;i<11;i++){ float h=mapD(ro+rd*t,ph); res=min(res,13.0*h/t); t+=clamp(h,0.05,0.36); if(res<0.03||t>maxT) break; } return clamp(res,0.0,1.0); } float ambientOcclusion(vec3 p,vec3 n,float ph){ float occ=0.0, sc=1.0; for(int i=1;i<=4;i++){ float h=0.024+0.085*float(i); occ+=(h-mapD(p+n*h,ph))*sc; sc*=0.60; } return clamp(1.0-1.70*occ,0.0,1.0); } vec3 zoneColor(float m){ if(m<1.5) return vec3(0.030,0.022,0.055); // obsidian core if(m<2.5) return vec3(0.015,0.070,0.115); if(m<3.5) return vec3(0.060,0.030,0.130); if(m<4.5) return vec3(0.110,0.025,0.090); return vec3(0.035,0.090,0.100); } // Thin-film interference: the glass shells shift hue with view angle, which is // where the obsidian-orrery look departs from the original's gold reliquary. vec3 thinFilm(float nv,float thickness){ float d=thickness*(1.0+0.85*(1.0-nv)); return 0.5+0.5*cos(TAU*(d+vec3(0.00,0.27,0.55))); } float distributionGGX(vec3 n,vec3 h,float rough){ float a=rough*rough, a2=a*a; float nh=max(dot(n,h),0.0), d=nh*nh*(a2-1.0)+1.0; return a2/max(PI*d*d,0.0001); } float geometrySchlickGGX(float nv,float rough){ float k=(rough+1.0); k=k*k*0.125; return nv/max(nv*(1.0-k)+k,0.0001); } float geometrySmith(vec3 n,vec3 v,vec3 l,float rough){ return geometrySchlickGGX(max(dot(n,v),0.0),rough) *geometrySchlickGGX(max(dot(n,l),0.0),rough); } vec3 fresnelSchlick(float hv,vec3 f0){ return f0+(1.0-f0)*pow(1.0-clamp(hv,0.0,1.0),5.0); } vec3 directBRDF(vec3 n,vec3 v,vec3 l,vec3 radiance,vec3 base, vec3 f0,float rough,float metal,float vis){ vec3 h=normalize(v+l); float nl=max(dot(n,l),0.0), nv=max(dot(n,v),0.0); vec3 f=fresnelSchlick(max(dot(h,v),0.0),f0); vec3 spec=distributionGGX(n,h,rough)*geometrySmith(n,v,l,rough)*f/max(4.0*nl*nv,0.001); return ((1.0-f)*(1.0-metal)*base/PI+spec)*radiance*nl*vis; } vec3 studioEnvironment(vec3 r){ float sky=clamp(0.5+0.5*r.y,0.0,1.0); vec3 env=mix(vec3(0.004,0.006,0.016),vec3(0.038,0.070,0.130),sky); float cool=pow(max(dot(r,normalize(vec3( 0.68,0.38,-0.62))),0.0),16.0); float warm=pow(max(dot(r,normalize(vec3(-0.60,0.50,-0.62))),0.0),26.0); return env+coolColor.rgb*cool*0.85+warmColor.rgb*warm*0.75; } vec3 filmicACES(vec3 x){ float a=2.51,b=0.03,c=2.43,d=0.59,e=0.14; return clamp((x*(a*x+b))/(x*(c*x+d)+e),0.0,1.0); } void main(){ vec2 uv=isf_FragNormCoord; vec2 px=1.0/RENDERSIZE; float ph=fract(TIME*speed/PERIOD); if(PASSINDEX==0){ // ============================================================ // PASS 0 — FORGE: raymarch the gyre, deformed by last frame's wake // ============================================================ vec2 sc=(gl_FragCoord.xy-0.5*RENDERSIZE.xy)/RENDERSIZE.y; // Read the wake once, before any marching — constant along this ray. gWake=clamp(dot(IMG_NORM_PIXEL(wake,uv).rgb,vec3(0.3333))*wakeGain,0.0,1.4); vec3 ro=vec3(0.30,0.34,-6.10); vec3 ta=vec3(0.0,0.02,0.0); vec3 fw=normalize(ta-ro); vec3 rt=normalize(cross(fw,vec3(0.0,1.0,0.0))); vec3 up=cross(rt,fw); vec3 rd=normalize(fw*1.60+rt*sc.x+up*sc.y); float t=0.0, hit=-1.0; vec4 res=vec4(0.0); for(int i=0;i<72;i++){ res=mapScene(ro+rd*t,ph); if(res.x<0.0009*max(1.0,t)){ hit=t; break; } t+=max(0.0022,res.x*0.55); if(t>9.0) break; } float vign=1.0-0.26*dot(sc,sc); vec3 col=mix(vec3(0.0015,0.0025,0.008),vec3(0.006,0.014,0.030), clamp(0.55+0.55*sc.y,0.0,1.0))*vign; float emitMask=0.0; if(hit>0.0){ vec3 p=ro+rd*hit; vec3 n=normalAt(p,ph); vec3 v=-rd; vec3 base=zoneColor(res.y); float ao=ambientOcclusion(p,n,ph); vec3 micro=sin(p*vec3(13.1,15.7,11.9)+vec3(0.0,1.7,3.1)); n=normalize(n+0.011*(micro-n*dot(micro,n))); float rough=clamp((0.16+0.055*res.z)*roughness,0.05,0.85); float metal=clamp(0.30+0.14*res.z+(metallic-0.55)*0.9,0.0,1.0); float nv=max(dot(n,v),0.0); vec3 film=thinFilm(nv,filmDepth*(0.42+0.20*res.z)+0.15*ph+0.30*gWake); vec3 f0=mix(vec3(0.045),film,0.55*min(filmDepth,1.0)); f0=clamp(f0+(metal-0.30)*(base-vec3(0.04)),vec3(0.02),vec3(0.95)); vec3 l1=normalize(vec3(-0.50,0.78,-0.38)); vec3 l2=normalize(vec3( 0.76,0.14,-0.54)); vec3 l3=normalize(vec3(-0.28,-0.70,0.48)); // Only the key light casts; fill lights are unshadowed (cheap, and the // glass reads better without competing shadow terms anyway). vec3 surf =directBRDF(n,v,l1,vec3(6.20,5.35,4.60),base,f0,rough,metal,softShadow(p+n*0.024,l1,ph,3.0)); surf+=directBRDF(n,v,l2,coolColor.rgb*5.40,base,f0,rough,metal,1.0)*0.90; surf+=directBRDF(n,v,l3,warmColor.rgb*4.00,base,f0,rough,metal,1.0)*0.60; vec3 envF=fresnelSchlick(nv,f0); vec3 env=studioEnvironment(reflect(-v,n)); surf+=env*(envF*(1.05+0.45*(1.0-rough))+base*(1.0-metal)*0.12)*ao; surf+=base*vec3(0.10,0.14,0.20)*ao; float edge=pow(1.0-nv,2.6); vec3 rim=mix(coolColor.rgb,warmColor.rgb,0.5+0.5*sin(TAU*(ph+0.11*res.z))); vec3 emission=mix(ignite.rgb,film,0.40)*res.w*(0.55+0.85*edge); surf=surf*ao*1.15+rim*edge*(0.45+0.55*ao)*film*1.70+emission*1.55; surf*=exp(-0.042*hit); col=surf; emitMask=clamp(res.w*0.85+edge*0.30,0.0,1.5); } gl_FragColor=vec4(max(col,0.0),emitMask); } else if(PASSINDEX==1){ // ============================================================ // PASS 1 — WAKE: curl-advected persistent trail fed by the forge // ============================================================ float frameStep=min(TIMEDELTA*60.0,2.0); vec2 p=uv-0.5; float aspect=RENDERSIZE.x/RENDERSIZE.y; p.x*=aspect; // Curl of the wake's own luminance — trails braid around each other. The // gradient is taken over a wide stencil: a one-pixel stencil on a sharp // float buffer gives chaotic per-pixel velocities that shred the trail // into speckle instead of advecting it. vec2 gd=px*3.0; float wxp=dot(IMG_NORM_PIXEL(wake,uv+vec2(gd.x,0.0)).rgb,vec3(0.3333)); float wxm=dot(IMG_NORM_PIXEL(wake,uv-vec2(gd.x,0.0)).rgb,vec3(0.3333)); float wyp=dot(IMG_NORM_PIXEL(wake,uv+vec2(0.0,gd.y)).rgb,vec3(0.3333)); float wym=dot(IMG_NORM_PIXEL(wake,uv-vec2(0.0,gd.y)).rgb,vec3(0.3333)); vec2 curl=vec2(-(wyp-wym),(wxp-wxm))*(2.0*wakeCurl); // Rotational field: the wake orbits the reliquary it was shed from. float r=length(p); vec2 spin=vec2(-p.y,p.x)*(0.85*exp(-2.2*r*r)); vec2 vel=curl+spin*wakeFlow; vel+=0.10*wakeFlow*vec2(sin(p.y*11.0+TIME*0.37),cos(p.x*9.0-TIME*0.31)); vel.x/=aspect; vec2 back=uv-vel*(0.0130*frameStep*wakeFlow); vec3 old =IMG_NORM_PIXEL(wake,back).rgb*0.66; old+=IMG_NORM_PIXEL(wake,back+vec2(px.x,0.0)).rgb*0.085; old+=IMG_NORM_PIXEL(wake,back-vec2(px.x,0.0)).rgb*0.085; old+=IMG_NORM_PIXEL(wake,back+vec2(0.0,px.y)).rgb*0.085; old+=IMG_NORM_PIXEL(wake,back-vec2(0.0,px.y)).rgb*0.085; old*=pow(wakeLife,frameStep); // Only the ignition ridges and grazing edges shed into the wake, softened // across a small stencil so what enters the flow is a plume, not a speck. vec4 f =IMG_NORM_PIXEL(forge,uv)*0.44; f+=IMG_NORM_PIXEL(forge,uv+vec2(px.x,0.0)*1.8)*0.14; f+=IMG_NORM_PIXEL(forge,uv-vec2(px.x,0.0)*1.8)*0.14; f+=IMG_NORM_PIXEL(forge,uv+vec2(0.0,px.y)*1.8)*0.14; f+=IMG_NORM_PIXEL(forge,uv-vec2(0.0,px.y)*1.8)*0.14; vec3 shed=f.rgb*f.a*(0.50*wakeGain); float boundary=smoothstep(0.0,0.03,uv.x)*smoothstep(0.0,0.03,1.0-uv.x) *smoothstep(0.0,0.03,uv.y)*smoothstep(0.0,0.03,1.0-uv.y); vec3 next=max(old,shed)*boundary; vec3 hot=max(next-vec3(0.72),vec3(0.0)); next=min(next,vec3(0.72))+hot/(1.0+0.85*hot); gl_FragColor=vec4(clamp(next,0.0,1.45),1.0); } else { // ============================================================ // PASS 2 — COMPOSITE: solid gyre over its own luminous exhaust // ============================================================ vec3 f=IMG_NORM_PIXEL(forge,uv).rgb; // Dispersed wake read: the trail refracts as it passes behind the glass. float sp=dispersion*px.x*2.2; vec3 w=vec3( IMG_NORM_PIXEL(wake,uv+vec2( sp, 0.0)).r, IMG_NORM_PIXEL(wake,uv).g, IMG_NORM_PIXEL(wake,uv-vec2( sp, 0.0)).b); // Wide, cheap bloom on the wake only. vec3 halo=vec3(0.0); halo+=IMG_NORM_PIXEL(wake,uv+vec2( 2.6,0.0)*px).rgb; halo+=IMG_NORM_PIXEL(wake,uv-vec2( 2.6,0.0)*px).rgb; halo+=IMG_NORM_PIXEL(wake,uv+vec2(0.0, 2.6)*px).rgb; halo+=IMG_NORM_PIXEL(wake,uv-vec2(0.0, 2.6)*px).rgb; halo+=IMG_NORM_PIXEL(wake,uv+vec2( 4.4, 4.4)*px).rgb; halo+=IMG_NORM_PIXEL(wake,uv-vec2( 4.4, 4.4)*px).rgb; halo*=0.1667; float wl=dot(w,vec3(0.3333)); vec3 trail=(w+halo*0.75)*bleed; // max()-accumulation pulls every channel toward parity, so the raw trail // reads as grey fur. Re-saturate, then roll off before it can white out. trail=max(mix(vec3(dot(trail,vec3(0.3333))),trail,1.50),0.0); trail+=mix(coolColor.rgb,warmColor.rgb,clamp(wl*1.6,0.0,1.0))*wl*0.55*bleed; trail=trail/(1.0+0.60*trail); // The forge sits in front; the wake glows around and through its gaps. float solid=clamp(dot(f,vec3(0.3333))*3.4,0.0,1.0); vec3 col=f+trail*(1.0-0.55*solid); col+=f*wl*0.30*bleed; // shells relit by their own exhaust col=filmicACES(col*1.06*exposure); col=pow(max(col,0.0),vec3(0.94)); gl_FragColor=vec4(col,1.0); } }
Armillary Wake: the nested harmonic-torus hierarchy of Nested Orbit Reliquary, unfolded from a coplanar eye onto mutually-orthogonal gyre axes and run through a global helical twist — an obsidian-glass orrery with prime carrier counts (3/7/13/23) that beat instead of nest. A persistent curl-advected wake buffer, in the two-way coupling spirit of Alchemic Current, catches the rings' ignition and feeds it back: the wake modulates ridge amplitude and ignition in the SDF, so the trails deform the metal that shed them.
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).