{ "summary": "Scour the web for unexpected jaw-dropping procedural particle-morph shapes, return implementable GLSL/math for the Vestige hero", "agentCount": 5, "logs": [], "result": { "pick": { "chosenShapes": [ { "name": "Aizawa Strange Attractor (chaotic-thought butterfly)", "whyChosen": "The single highest 'how did they DO that' shape. 40k particles self-organize from noise into a glowing toroidal shell pierced by an axial spike - a sculpture no primitive can fake. Deterministic chaos pools particles where the orbit lingers, giving free non-uniform nebula density that beats any Perlin shading. It is the literal 'watch your agent think' money shot: same seed = same orbit, yet the path looks unpredictable. Closed-form per-seed via fixed-step iteration, no CPU LUT or mesh needed.", "glslSnippet": "// Aizawa attractor - iterate the ODE from a seed-jittered start.\n// Each particle lands on a different part of the same attracting set,\n// and varying the step count spreads density along the orbit.\nvec3 shapeAizawa(float a, float b, float c, float S){\n // tiny jittered start ball near origin (seeds decorrelated by hashing)\n vec3 p = (vec3(fract(a*43.0+0.13), fract(b*91.7+0.41), fract(c*57.3+0.77)) - 0.5) * 0.12;\n const float A=0.95, B=0.7, C=0.6, D=3.5, E=0.25, F=0.1, dt=0.01;\n int iters = 260 + int(a*140.0); // 260..400 -> spread along manifold\n for(int i=0;i<400;i++){\n if(i>=iters) break; // GLSL ES: constant loop bound + break\n float dx = (p.z - B)*p.x - D*p.y;\n float dy = D*p.x + (p.z - B)*p.y;\n float dz = C + A*p.z - p.z*p.z*p.z/3.0\n - (p.x*p.x + p.y*p.y)*(1.0 + E*p.z)\n + F*p.z*p.x*p.x*p.x;\n p += vec3(dx,dy,dz)*dt;\n }\n // attractor lives roughly in [-1.5,1.5]; 0.42*S fills ~70% of frustum height\n return p * (0.42 * S);\n // PALETTE: color by length(velocity) of the final step ->\n // violet (slow shell) -> cyan -> gold (fast axial spike).\n}", "seedsNeeded": "a (drives iteration count + start jitter), b, c (start jitter). All three used.", "scaleNote": "Attractor spans ~[-1.5,1.5]. Multiply by 0.42*S so the full body fills ~70% of frustum height. Cap loop at 400 for 40k @ 60fps; 260-400 iters per particle is the sweet spot for even density." }, { "name": "Hopf Fibration (linked memories, interlocking rings)", "whyChosen": "The most 'premium math' object you can ship: nested Villarceau circles threaded through each other so every ring links every other exactly once, yet none collide. Reads as impossible 3D chainmail of light that rotates into self-similar tori. Devs who know it gasp; devs who don't think it's an alien artifact. Perfect on-brand metaphor for a fully-connected memory graph that never tangles. Pure closed-form from seeds, surface-coherent.", "glslSnippet": "// Hopf fibration: seed -> a base point on S^2 (which fiber), then a phase\n// along that fiber circle; lift to S^3 and stereographically project to R^3.\n// Quantizing the base point to a few values yields a few BIG clean linked\n// rings (more legible than a full fill).\nvec3 shapeHopf(float a, float b, float c, float S){\n const float TAU = 6.28318530718;\n // quantize base point so we get ~12 bold interlocking rings, not mush\n float ringId = floor(a * 12.0);\n float cosT = 2.0*fract(ringId*0.61803398875) - 1.0; // base colatitude, spread\n float baseTheta= acos(clamp(cosT,-1.0,1.0));\n float basePhi = TAU * fract(ringId*0.7548776662);\n vec3 bp = vec3(sin(baseTheta)*cos(basePhi),\n sin(baseTheta)*sin(basePhi),\n cos(baseTheta)); // (x,y,z) on S^2\n float t = TAU * b; // phase along the fiber circle\n float k = 1.0 / sqrt(2.0*(1.0 + bp.z) + 1e-4);\n vec4 P = vec4((1.0+bp.z)*cos(t),\n bp.x*sin(t) - bp.y*cos(t),\n bp.x*cos(t) + bp.y*sin(t),\n (1.0+bp.z)*sin(t)) * k; // point on S^3\n vec3 q = P.xyz / (1.0 - P.w + 1e-3); // stereographic S^3 -> R^3\n // jitter slightly along c so each ring has a little tube thickness\n q += (vec3(fract(c*71.3),fract(c*131.7),fract(c*197.1))-0.5)*0.04;\n return q * (0.28 * S);\n}", "seedsNeeded": "a (picks which of ~12 rings), b (phase along the fiber circle), c (tube-thickness jitter). All three used.", "scaleNote": "Stereographic output ranges widely; 0.28*S tames it to ~70% frustum. The 1e-3 guard on (1-P.w) prevents the projection blowing up at the far pole. Use ~8-16 quantized rings for legibility - a full continuous fill reads as fog." }, { "name": "(p,q) Torus Knot tube (the reasoning loop)", "whyChosen": "A glowing rope that winds a torus and ties itself into a perfect trefoil - clean, logo-grade, obviously computed-not-drawn. Inflating the core curve into a particle TUBE (Frenet frame + uniform disc fill) makes it read as a solid living rope under additive bloom, not a thin wire. Bump (p,q) each loop (2,3 -> 3,2 -> 3,4 -> 5,2) and the SAME code morphs through a family of distinct knots: free variety. On-brand as 'the agent threading recall back through prior context.'", "glslSnippet": "// (p,q) torus knot, inflated into a volumetric tube via a Frenet-ish frame.\n// a -> position along the curve; b,c -> uniform fill of the tube cross-section.\nvec3 shapeTorusKnot(float a, float b, float c, float S){\n const float TAU = 6.28318530718;\n const float P = 3.0, Q = 7.0; // (3,7) braid; swap per loop for variety\n const float R = 1.0, rMinor = 0.45, rTube = 0.16;\n float t = a * TAU;\n float cq = cos(Q*t), sq = sin(Q*t);\n float ring = R + rMinor*cq;\n vec3 C = vec3(ring*cos(P*t), ring*sin(P*t), rMinor*sq); // core curve\n // analytic tangent dC/dt\n vec3 T = vec3(-rMinor*Q*sq*cos(P*t) - ring*P*sin(P*t),\n -rMinor*Q*sq*sin(P*t) + ring*P*cos(P*t),\n rMinor*Q*cq);\n T = normalize(T);\n vec3 N = normalize(cross(T, vec3(0.0,0.0,1.0)));\n vec3 B = cross(T, N);\n float ang = b * TAU;\n float rad = sqrt(c) * rTube; // sqrt -> uniform disc (no center clump)\n vec3 pos = C + rad*(cos(ang)*N + sin(ang)*B);\n return pos * (0.55 * S);\n}", "seedsNeeded": "a (along the knot), b (tube angle), c (radial fill, sqrt for uniform disc). All three used.", "scaleNote": "Knot body spans roughly [-1.45,1.45]; 0.55*S fills ~70% height. Keep P,Q coprime (3&7, 2&5, 3&4) or the curve degenerates. rTube=0.16 gives a solid rope; drop to 0.05 for a bare hypnotic wire." }, { "name": "DNA Double Helix (memory as encoded sequence)", "whyChosen": "Instantly legible at thumbnail size - twin spiraling ribbons with rungs ladder-stepping between them, the universal 'intelligent / alive' signal. Photographs perfectly for a hero still and rotates beautifully. Provides recognizable, calm contrast to the abstract chaos/topology shapes (variety). Built-in palette story: strand A cyan, strand B violet, base-pair rungs gold. On-brand: event strand + interpretation strand bound by link base-pairs.", "glslSnippet": "// DNA double helix: two antiparallel sugar-phosphate strands + base-pair rungs.\n// a -> height along the axis; b classifies particle into strandA / strandB / rung;\n// c -> radial thickness so strands read as ribbons, not lines.\nvec3 shapeDNA(float a, float b, float c, float S){\n const float TAU = 6.28318530718;\n const float TWISTS = 6.0; // full turns over the length\n const float HEIGHT = 2.0;\n const float RADIUS = 0.55;\n float yy = (a - 0.5) * HEIGHT;\n float ang = a * TWISTS * TAU;\n vec3 strandA = vec3(cos(ang)*RADIUS, yy, sin(ang)*RADIUS);\n vec3 strandB = vec3(cos(ang+3.14159)*RADIUS, yy, sin(ang+3.14159)*RADIUS);\n vec3 pos;\n if(b < 0.42){ // strand A (cyan)\n pos = strandA;\n } else if(b < 0.84){ // strand B (violet)\n pos = strandB;\n } else { // base-pair rung (gold), interpolate across the ladder\n float k = fract(b * 53.0);\n pos = mix(strandA, strandB, k);\n }\n // ribbon thickness: jitter in a small ball, sqrt for even fill\n float rad = sqrt(c) * 0.06;\n float ja = fract(c*131.7)*TAU, jb = fract(c*71.3)*3.14159;\n pos += rad * vec3(sin(jb)*cos(ja), cos(jb), sin(jb)*sin(ja));\n return pos * (0.62 * S);\n}", "seedsNeeded": "a (height + twist angle), b (strandA / strandB / rung classifier), c (ribbon thickness jitter). All three used.", "scaleNote": "Height = 2.0, radius = 0.55 -> bounding box ~1.1 wide x 2.0 tall. 0.62*S puts the tall axis at ~70% frustum height (this one is portrait-oriented, which frames well for a hero). Tune TWISTS=6 for a classic look; raise to 8-10 for a tighter coil." }, { "name": "Spiral Galaxy (galaxy of memories)", "whyChosen": "The most universally read 'whoa' shape - 40k points snap into a barred spiral with a luminous gold core and trailing violet dust arms, like a Hubble plate assembling. Reads even at thumbnail size and needs zero math literacy from the viewer, so it converts the broadest audience. Exponential vertical falloff gives a real galactic-disc thinness, not a flat pancake. On-brand: each particle a stored memory orbiting the bright working-context core.", "glslSnippet": "// Barred spiral galaxy: log-spiral arms + exponential disc thickness + bright core.\n// a -> which arm + per-arm scatter; b -> radius (sqrt for centre concentration);\n// c -> vertical disc jitter and core bulge.\nvec3 shapeGalaxy(float a, float b, float c, float S){\n const float TAU = 6.28318530718;\n const float N_ARMS = 4.0;\n const float TWIST = 2.6; // arm winding\n float arm = floor(a * N_ARMS);\n float t = sqrt(b); // 0..1 radial, sqrt concentrates centre\n float r = t;\n float jit = (fract(a*97.13) - 0.5) * 0.35 * (1.0 - t); // arms tighten outward\n float ang = arm * TAU / N_ARMS + t * TWIST * TAU + jit;\n // disc thickness: thick bulge at centre, thin at rim\n float yy = (fract(c*53.7) - 0.5) * 0.14 * exp(-2.5*r);\n vec3 pos = vec3(cos(ang)*r, yy, sin(ang)*r);\n // central bulge: a fraction of particles form the spherical core\n if(b < 0.06){\n float rr = pow(fract(c*191.3), 0.5) * 0.12;\n float th = fract(a*311.7)*TAU, ph = acos(2.0*fract(c*131.1)-1.0);\n pos = vec3(rr*sin(ph)*cos(th), rr*cos(ph), rr*sin(ph)*sin(th));\n }\n return pos * (1.30 * S);\n // PALETTE: gold core (small r) -> emerald mid-disc -> violet/cyan rim by r.\n}", "seedsNeeded": "a (arm index + angular scatter), b (radius; <0.06 -> core bulge), c (disc thickness + core sphere jitter). All three used.", "scaleNote": "Disc radius normalized to ~1.0; 1.30*S fills ~70% frustum width for the flat disc (it presents widescreen). Tilt the whole shape ~25deg on the engine side so the spiral reads as a disc, not a ring. exp(-2.5*r) keeps the rim a razor-thin sheet." }, { "name": "3D Superformula Supershape (the morph engine)", "whyChosen": "ONE equation, four knobs -> starfish, sea-urchin, faceted crystal, blooming flower, bloated pollen. Because it is the same target with different params, you get an entire alien bestiary nearly free, and morphing BETWEEN parameter sets is buttery since the topology is shared - perfect connective tissue between the 'serious' shapes. A dev watching it shapeshift instantly realizes there is no mesh: it is pure procedural magic. On-brand: one generator, infinite forms = one memory engine producing emergent structure.", "glslSnippet": "// Gielis superformula, spherical product of two superformulas (surface-sampled).\n// a -> longitude theta, b -> latitude phi, c -> thin surface-shell jitter.\n// Swap the (m,n1,n2,n3) set per loop to morph through the bestiary.\nfloat superR(float ang, float m, float n1, float n2, float n3){\n float t = m * ang * 0.25;\n float c1 = pow(abs(cos(t)), n2);\n float c2 = pow(abs(sin(t)), n3);\n return pow(c1 + c2 + 1e-6, -1.0/n1);\n}\nvec3 shapeSupershape(float a, float b, float c, float S){\n const float PI = 3.14159265359;\n float theta = (a*2.0 - 1.0) * PI; // -PI..PI\n float phi = (b - 0.5) * PI; // -PI/2..PI/2\n // spiky sea-urchin: m=7,n1=0.2,n2=1.7,n3=1.7\n // (crystal m=4,n1=60,n2=55,n3=30 | star m=5,n1=0.1,n2=1.7,n3=1.7)\n float m=7.0, n1=0.2, n2=1.7, n3=1.7;\n float r1 = superR(theta, m, n1, n2, n3);\n float r2 = superR(phi, m, n1, n2, n3);\n vec3 pos = vec3(r1*cos(theta) * r2*cos(phi),\n r1*sin(theta) * r2*cos(phi),\n r2*sin(phi));\n // tame extreme spikes so framing stays stable, then add a thin shell jitter\n pos = normalize(pos) * pow(length(pos), 0.6);\n pos *= (1.0 + (c - 0.5)*0.04);\n return pos * (0.85 * S);\n}", "seedsNeeded": "a (longitude), b (latitude), c (surface-shell thickness). All three used. (superR is a free helper - paste once.)", "scaleNote": "After the normalize()*pow(len,0.6) spike-taming the body sits in ~[-1.6,1.6]; 0.85*S -> ~70% height. The +1e-6 inside superR and abs() on cos/sin keep pow() real for odd exponents on negatives. Lerp the (m,n1,n2,n3) tuple over a loop for a living bloom." }, { "name": "VESTIGE text beat (baked-mask glyph cloud)", "whyChosen": "A skeptical dev expects abstract blobs; spelling the product name out of 40k live particles is the single biggest 'wait, how' moment - recognizable signal emerging from noise - and it states the value prop in one beat (REMEMBER assembles, FORGET dissolves to dust). It also signals 'this team can actually drive the GPU.' The production-correct method is zero-rejection: bake every inside-pixel of the rasterized word into the position texture once on the CPU, so each particle already owns a guaranteed on-glyph target. The snippet is the SHADER-side fallback that rejection-samples a bound alpha mask when you prefer a pure-GPU path.", "glslSnippet": "// TEXT beat. PREFERRED (zero rejection): on the CPU, draw the word to a canvas,\n// read alpha, collect every pixel with a>0.5 into inside[], and for particle i\n// write target = vec3((px/W-0.5)*S*aspect, -(py/H-0.5)*S, (hash-0.5)*depth)\n// straight into the position FBO. No per-frame cost.\n//\n// SHADER fallback below: sample a bound alpha mask uMask (NearestFilter) and\n// relax outside particles toward the glyph along the alpha gradient.\nuniform sampler2D uMask; // word rasterized to alpha; e.g. 1024x256\nuniform float uAspect; // mask width/height\nvec3 shapeText(float a, float b, float c, float S){\n const float DEPTH = 0.10;\n vec2 uv = vec2(a, b); // candidate glyph UV\n // a few relaxation steps: walk toward denser alpha so strays land on letters\n for(int i=0;i<4;i++){\n float al = texture2D(uMask, uv).a;\n if(al > 0.5) break;\n float e = 1.0/256.0;\n float gx = texture2D(uMask, uv+vec2(e,0.0)).a - texture2D(uMask, uv-vec2(e,0.0)).a;\n float gy = texture2D(uMask, uv+vec2(0.0,e)).a - texture2D(uMask, uv-vec2(0.0,e)).a;\n vec2 g = vec2(gx,gy);\n uv += normalize(g + 1e-5) * 0.03; // climb the alpha field\n }\n vec3 pos = vec3((uv.x - 0.5) * uAspect, // centre + correct aspect\n -(uv.y - 0.5), // flip: texture v is top-down\n (c - 0.5) * DEPTH); // slab depth so it isn't flat\n return pos * (0.95 * S);\n // PALETTE: glyph core gold/emerald; edge/stray particles cyan->violet,\n // driven by step(uProgress, noise) for the assemble/dissolve dust.\n}", "seedsNeeded": "a, b (candidate glyph UV / which inside-pixel), c (slab depth). All three used. Requires uniforms uMask (alpha atlas of the word) + uAspect.", "scaleNote": "0.95*S sizes the word to ~70% frustum width (text reads best wide). uAspect = mask_w/mask_h keeps letters un-stretched. The 4-step alpha-climb is a cheap fallback; for crisp edges and zero runtime cost, prefer the CPU-baked inside[] -> position-FBO path described in the header comment (the nicoptere/barradeau method)." } ] }, "sources": [ "https://www.songho.ca/opengl/gl_torus.html", "https://www.algosome.com/articles/aizawa-attractor-chaos.html", "https://en.wikipedia.org/wiki/Thomas%27_cyclically_symmetric_attractor", "https://paulbourke.net/geometry/supershape/", "https://analyticphysics.com/Higher%20Dimensions/Visualizing%20Calabi-Yau%20Manifolds.htm", "https://nilesjohnson.net/hopf.html", "https://mathworld.wolfram.com/KleinBottle.html", "https://paulbourke.net/geometry/sphericalh/", "https://www.shadertoy.com/view/tfdBDf", "https://en.wikipedia.org/wiki/Lorenz_system", "https://en.wikipedia.org/wiki/List_of_chaotic_maps", "https://paulbourke.net/geometry/supershape/", "https://kenbrakke.com/evolver/examples/periodic/gyroid/gyroid.html", "https://en.wikipedia.org/wiki/Torus_knot", "https://ykob.github.io/sketch-threejs/sketch/dna.html", "https://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/", "https://www.dynamicmath.xyz/strange-attractors/", "https://en.wikipedia.org/wiki/Hopf_fibration", "https://en.wikipedia.org/wiki/Gyroid", "https://en.wikipedia.org/wiki/Torus_knot", "https://paulbourke.net/geometry/supershape/", "https://en.wikipedia.org/wiki/Forgetting_curve", "https://en.wikipedia.org/wiki/Space_colonization", "https://github.com/nicoptere/FBO and https://barradeau.com/blog/?p=621 (baked image->FBO positions); https://tympanus.net/codrops/2026/01/28/webgpu-gommage-effect-dissolving-msdf-text-into-dust-and-petals-with-three-js-tsl/ and https://github.com/Chlumsky/msdfgen (MSDF >0.5 inside test, Jan 2026)", "https://www.algosome.com/articles/aizawa-attractor-chaos.html (Aizawa eqs); https://www.geeks3d.com/20140630/lorenz-attractor-butterfly-effect/ (Lorenz GLSL); https://github.com/BrutPitt/glChAoS.P (256M-particle GPU attractor reference)", "https://paulbourke.net/geometry/supershape/ (exact 3D spherical-product eqs); https://github.com/Softwave/glsl-superformula (GLSL function); https://en.wikipedia.org/wiki/Superformula", "https://en.wikipedia.org/wiki/Gyroid (G = sinXcosY+sinYcosZ+sinZcosX); https://alessandromastrofini.it/2022/03/29/gyroid-and-minimal-surfaces-for-cad/ (TPMS for CAD, gradient/level-set form)", "https://paulbourke.net/geometry/sphericalh/ (r=f(theta,phi) spherical harmonic surface, the m0..m7 exponent form); https://github.com/sebh/HLSL-Spherical-Harmonics (shader SH functions)", "https://www.4rknova.com/blog/2025/09/01/mandelbulb (2025 DE walkthrough, power-8); http://2008.sub.blue/projects/mandelbulb.html (Beddard, the canonical DE formula); https://en.wikipedia.org/wiki/Mandelbulb", "https://www.songho.ca/opengl/gl_torus.html (torus knot + trefoil parametric); https://rwoodley.org/posts/12-TorusKnots.html (p,q math); https://threejs.org/docs/pages/TorusKnotGeometry.html" ] } }