05 · Motion, 3D and shader craft that ships in a no-build static page (2026)

Research for the ETS27 concept round. Lens: what the award-level sites actually do with motion, 3D and shaders, which of it is feasible in one self-contained index.html with pinned CDN imports, what it costs, and what the headless render lane (Chrome for Testing, ANGLE Metal) can and cannot show. Everything marked "proven" was run today, 2026-10-14, in that exact lane; screenshots of the reference sites live in concepts/research/refs/<slug>/.

1. What I studied

Rendered headlessly at 1440×900 (plus 390 wide) with concepts/tools/render.cjs; gated sites were pushed through with a scratch click script.

| Slug | Site | Why it is here | Headless result | |---|---|---|---| | lusion | https://lusion.co | Studio behind Oryzo; framed WebGL stage, physical materials | Renders fully | | igloo-inc | https://www.igloo.inc | Awwwards Site of the Year 2024; monochrome terrain, volumetric fog, HUD overlay | Renders fully | | astrodither | https://astrodither.robertborghesi.is/ | Awwwards SOTD 2026-05-05; WebGPU + TSL, glyph-cell dithering, "hold for speed" | Click gate; captured after a scripted click | | matvoyce | https://matvoyce.tv | SOTD + GSAP Site of the Year 2025 nominee; kinetic type with GSAP only | Renders fully (7,245 px page) | | oryzo | https://oryzo.ai | Awwwards Site of the Month April 2026 + Developer Award; one hero object, cinematic light | Renders fully (56,691 px page) | | iventions | https://iventions.com | SOTD + Developer Award, CSSDA Site of the Month Oct 2025; spotlight metaphor | Renders fully | | ivress | https://brand.ivress.co.jp | WebGPURenderer with WebGL fallback and TSL (per Utsubo's write-up) | Preloader stalls at 99 % headless | | active-theory | https://activetheory.net | The preloader itself is a contour-field shader | Preloader stalls at "/75" headless | | unseen | https://unseen.co | Audio gate pattern | "Enter" gate; navigation timed out at 60 s | | flowfield-gpgpu | https://flowfield.niccolofanton.dev/ | GPGPU flow-field particles that morph between shapes | Renders fully | | blobmixer | https://blobmixer.14islands.com/ | The origin of the iridescent-blob cliché (2021), included as the thing to avoid | Renders fully | | fragments-supply | https://www.fragments.supply/ | Shader-course site; KIFS raymarch tunnel as a framed panel | Renders fully |

Text sources, all read today: Webflow's "GSAP becomes free" announcement and the GSAP 3.15 README/license page; the Lenis 1.3.26 README; web.dev "WebGPU is now supported in major browsers"; Chrome's scroll-driven animations guide and the Interop 2026 notes; caniuse feature JSON pulled from GitHub raw; the Codrops "Efecto" article on real-time ASCII and dithering (fetched through url-ingest because Codrops blocks plain fetches); Utsubo's "Best three.js websites 2026" and Hon Tran's juror list for 2026 winners; the three.js forum raymarching and GPGPU showcases.

2. What actually works, and why

Lusion frames the 3D instead of flooding the viewport. The WebGL stage is a 1,296×640 rounded panel inside a cool lavender page (#eef0f8), with a 36 px light-weight Aeonik headline above it and "+" registration marks under it. Three materials only (matte white, gloss black, cobalt), soft shadows, physics-weighted motion. The frame is why it still reads as a page: type stays type, the scene stays a scene.

Igloo makes one material and one light the whole idea. Full-bleed monochrome terrain with volumetric fog, ice blocks lit from inside, and a monospace HUD layer (// Copyright, measurement lines with numbers 40-37-33-21-30). Nothing is coloured; the glow is the only accent. The overlay typography is set at 12–13 px mono with generous tracking, so the scene feels instrumented, not decorated.

Astrodither turns 3D into print. A hand rendered as a grid of glyph cells (filled square, ring, cross, dot) chosen by luminance, in hot pink #FF4FA3 and violet on #0B0B12, with coloured particle streaks flying past. The HUD reads 00:13:449 and HOLD FOR SPEED | 1.00x: the interaction is a verb printed on screen. WebGPU with TSL, custom fluid sim, selective bloom. The dithered look is how it avoids the glossy-render cliché while still being 3D.

Mat Voyce proves type alone is a hero image. F37 Judge condensed italic at roughly 330 px cap height in a single cyan #00c8ff on cream #fffdf6, one acid-green pill, one rotated card. Every move is a GSAP timeline: letters stretch, snap and recombine on scroll. No WebGL anywhere. The restraint (one hue, one family, two weights) is what makes 330 px type feel designed rather than loud.

Oryzo is scroll as a film timeline. One cork coaster, physically lit on a cutting mat, cream Halyard Display at ~130 px, warm palette. The page is 56,691 px tall because every scroll position is a staged frame; the second state dims to a dark room with the wordmark as a watermark. Lesson: the hero object never changes, the light and camera do.

Iventions makes a flat page feel lit. A lavender light cone (a clip-path polygon) sweeps across cream; the giant "IVENTIONS" wordmark is violet where the cone hits and beige where it does not; an acid-yellow "stage" card floats at the apex. Inner pages tilt pastel panels (mint, lilac) a few degrees. The lighting metaphor is done with flat 2D shapes, which is exactly why it is cheap and sharp.

The flow field morphs between meanings. Hundreds of thousands of GPU particles advected by 4D simplex noise, with named presets (Dust, Caravaggio, Van Gogh, Helix, Nova) that the particles re-form into. The trick that makes it feel intelligent is not the noise, it is the morph targets: the field has somewhere to go.

Active Theory spends the preloader. Before anything loads you get contour lines and a dithered disc counting "/75": the first shader moment is the loader. Ivress does the same with a face at "99 %". Both stall in headless, which tells you they wait on a real-time or gesture condition.

3. What has become cliché (do not ship)

4. Technique catalogue with recipes, pins, budgets and references

All pins resolved on jsdelivr today. Compressed sizes are what the wire carries with Accept-Encoding: gzip.

| Asset | Pinned URL | gz | |---|---|---| | three (WebGL build, self-hosted) | ../../prototype/vendor/three.module.js + three.core.js (0.180) | 79 KB | | three WebGPU build | https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.webgpu.js | 160 KB (min) | | three TSL | https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.tsl.js | 6 KB | | three addons root | https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/ | per file | | GSAP core | https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js | 28 KB | | ScrollTrigger · SplitText · Flip · ScrollSmoother | same base, ScrollTrigger.min.js etc. | 18 · 3.6 · 9.7 · ~5 KB | | Lenis | https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.min.js (+ lenis.css) | 5.4 KB | | pmndrs postprocessing (only if you want its effect library) | https://cdn.jsdelivr.net/npm/postprocessing@6.39.5/build/index.js | 158 KB |

Latest upstream is three 0.186.1 and gsap 3.15.0; stay on 0.180 for three because the vendor copy is 0.180 and addons must match the core version exactly. Never put three.module.js and three.webgpu.js in the same import map.

4.1 GSAP 3.15 (free, all plugins) + ScrollTrigger + SplitText + Lenis — proven

Licence, verified on the package and gsap.com/standard-license: since Webflow's acquisition the whole toolset, including the former Club plugins, is free for commercial use; the only prohibited use is building a no-code visual animation builder that competes with Webflow. gsap-trial is deprecated; use gsap. Every plugin file resolves on jsdelivr at dist/<Plugin>.min.js (checked: ScrollTrigger, SplitText, ScrollSmoother, Flip, MorphSVG, DrawSVG, MotionPath, Observer, CustomEase, Draggable, Inertia, Physics2D, ScrambleText, Text, ScrollTo).


<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.css">
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger, SplitText);
const lenis = new Lenis({ lerp: 0.1 });            // honours prefers-reduced-motion by itself
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add(t => lenis.raf(t * 1000));
gsap.ticker.lagSmoothing(0);
document.fonts.ready.then(() => {
  SplitText.create('h1', { type: 'chars,words', mask: 'chars', autoSplit: true,
    onSplit: self => gsap.from(self.chars, { yPercent: 110, stagger: 0.03, duration: 0.9, ease: 'expo.out' }) });
});
</script>

Proven in the lane: gsap.version 3.15.0, 12 masked chars, one ScrollTrigger, zero console errors. Budget: 55 KB gz for the four files; SplitText re-splits on resize for free with autoSplit. Use Lenis only when a canvas has to track scroll; for a pure DOM page skip it and let the browser scroll. ScrollSmoother is the GSAP-native alternative (13 KB) if you want its data-speed parallax attributes.

Reference: Mat Voyce (type timelines), By-Kin (transitions that read as one continuous surface).

4.2 CSS scroll-driven animations — proven, zero JS


.progress { animation: grow linear both; animation-timeline: scroll(root); transform-origin: left; }
.reveal   { animation: fade linear both; animation-timeline: view(); animation-range: entry 0% cover 40%; }
.weight   { font-family: Fraunces; font-weight: 100; animation: thicken linear both;
            animation-timeline: view(); animation-range: entry 0% cover 60%; }
@keyframes thicken { to { font-weight: 900; font-variation-settings: 'opsz' 9, 'SOFT' 0, 'WONK' 0; } }

Support: Chrome/Edge 115 (July 2023), Safari 26 (September 2025; threaded in 26.4). Firefox is reported still behind layout.css.scroll-driven-animations.enabled as of Firefox 152, so wrap in @supports (animation-timeline: scroll()) and leave the static end state as the fallback. Transform and opacity run on the compositor; font-variation-settings, letter-spacing and anything that relayouts run on the main thread, which is fine for one 400 pt word and wrong for forty cards. Proven in the lane: progress bar at 50 % at mid-scroll, Fraunces moving 100→900 with the optical size dropping to 9.

4.3 View Transitions — proven same-document


document.startViewTransition(() => swapFromForkToMap());
/* CSS */ .lockup { view-transition-name: ets-lockup; }  ::view-transition-old(ets-lockup) { animation-duration: .6s; }

The fork → map and fork → mind switches are exactly this: the lockup keeps its view-transition-name, so the title physically flies from centre stage into the nav while the rest cross-fades. Same-document support is Chrome 111, Safari 18, Firefox 144 (caniuse: 92.9 % global). Cross-document (@view-transition { navigation: auto; }) is Chrome 126, Safari 18.2, Firefox 144 and needs no JS at all; use it for the multi-page standard route. document.startViewTransition exists in the render lane; the tool does not capture mid-transition frames, so judge the end states.

4.4 Variable-font animation — proven

Fraunces loads from Google Fonts with four axes (opsz 9..144, wght 100..900, SOFT 0..100, WONK 0..1) and animates with plain CSS keyframes or GSAP tweening fontVariationSettings. Cost is a text relayout per frame per element: keep it to a handful of display elements, never body copy. Use it where it means something: optical size falling as a word approaches, softness rising with proximity, weight tracking scroll depth. Reference: Oryzo's Halyard Display variable, the Fontwerk site (Typography Honors, 2025).

4.5 Fullscreen fragment shader: raymarched SDF with ordered dither — proven, no library

Forty lines of raw WebGL2, no three.js, is enough for a "mind-scape": a smooth-min blend of signed-distance primitives, 64 sphere-tracing steps, one directional light, Bayer 4×4 threshold at the end so the surface prints as two or three flat colours instead of a glossy render.


float map(vec3 p){ float a = sdSphere(p-vec3(0,0,3),1.0), b = sdSphere(p-vec3(sin(t)*1.2,.6,3.2),.6);
  float k=.6, h=clamp(.5+.5*(b-a)/k,0.,1.); return mix(b,a,h)-k*h*(1.-h); }   // smooth union
for(int i=0;i<64;i++){ float d=map(ro+rd*s); if(d<.001||s>10.) break; s+=d; }
int x=int(gl_FragCoord.x)%4, y=int(gl_FragCoord.y)%4; float th=(bayer[y*4+x]+.5)/16.;
col = step(vec3(th), col);                                                       // ordered dither

Budget: 1440×900 at DPR 1 is 1.3 M pixels × 64 steps ≈ 83 M distance evaluations per frame, comfortable on Apple silicon and mid Windows laptops; on phones render the canvas at 0.5–0.66 of CSS size with DPR capped at 1.5 and cut to 48 steps. Pause with IntersectionObserver and visibilitychange. The dither also hides banding, so you can drop to mediump on mobile. Reference: Fragments' KIFS tunnel (framed, right panel), the three.js forum "Raymarching SDF shapes, soft-min colour blends" (Feb 2026).

4.6 three.js WebGL (vendor build) + post-processing


<script type="importmap">{"imports":{
  "three": "../../prototype/vendor/three.module.js",
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"}}</script>
<script type="module">
import * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';   // 3.5 KB gz
</script>

three.module.js imports ./three.core.js relatively, so both vendor files must stay side by side. Addons from the CDN work with the vendor core because the versions match (0.180.0). Budget: 79 KB gz for the core; renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) on desktop and 1.5 on phones; one post pass costs roughly a full-screen draw per effect, so bloom + one custom pass is the ceiling for a hero.

4.7 three.js WebGPURenderer + TSL with automatic WebGL 2 fallback — proven both ways


<script type="importmap">{"imports":{
  "three":        "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.webgpu.js",
  "three/webgpu": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.webgpu.js",
  "three/tsl":    "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.tsl.js",
  "three/addons/":"https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/"}}</script>
<script type="module">
import * as THREE from 'three/webgpu';
import { pass, mx_noise_float, positionLocal, normalLocal, time, color, mix, uniform } from 'three/tsl';
import { bloom } from 'three/addons/tsl/display/BloomNode.js';
const renderer = new THREE.WebGPURenderer({ antialias: true /*, forceWebGL: true to test the fallback */ });
await renderer.init();                                   // required before the first frame
const mat = new THREE.MeshStandardNodeMaterial({ roughness: .6 });
const n = mx_noise_float(positionLocal.mul(2.2).add(time.mul(.4)));
mat.positionNode = positionLocal.add(normalLocal.mul(n.mul(uniform(.35))));
mat.colorNode = mix(color('#ff4fa3'), color('#ffd400'), n.add(.5));
const post = new THREE.PostProcessing(renderer);
const scenePass = pass(scene, camera), c = scenePass.getTextureNode('output');
post.outputNode = c.add(bloom(c, .6, .4, .85));
renderer.setAnimationLoop(() => post.render());
</script>

Measured in the lane: WebGPU backend initialised in 148 ms; the forced WebGL 2 backend took 3,410 ms to compile the same TSL graph, and both drew the identical frame. That 3.4 s is the real cost of the fallback: on Firefox (WebGPU only on Windows since 141) and on any machine that fails adapter request, the first frame arrives late, so the page must open on a CSS/SVG first frame and let the canvas fade in. Payload is 160 KB gz for the WebGPU build versus 79 KB for the WebGL build; only take it if you want compute shaders or TSL's one-graph-two-backends. Browser reach: Chrome/Edge 113+, Safari 26 (macOS Tahoe, iOS 26), Firefox 141 on Windows; caniuse data pulled today puts full support at 85.8 % globally. Reference: Astrodither, Ivress (WebGPURenderer with fallback), Utsubo's migration notes on the "one-line renderer swap".

4.8 GPGPU particles and flow fields

GPUComputationRenderer (addons/misc, 3.9 KB gz) ping-pongs two float textures (position, velocity); EXT_color_buffer_float is present in the lane and on every WebGL 2 browser that matters. 256×256 = 65,536 particles costs two full-screen compute passes at 256² plus one Points draw; 512×512 = 262,144 is the desktop ceiling for a hero at 60 fps; phones get 128×128. Store a per-particle seed in the position texture's alpha, advect velocity with 4D simplex noise (the fourth axis is time, so the field evolves instead of scrolling), and give every state a target-position texture so the field can morph into a shape (the day-2 agenda, the Austin skyline, the word FREE). On WebGPU the same thing is a TSL compute node and scales to millions, but the WebGL path is enough. Reference: flowfield.niccolofanton.dev, Shopify Editions Spring 2026 (type dispersing into particles).

4.9 ASCII / glyph-cell and dither renderers

Two different costs, do not confuse them. Ordered (Bayer or blue-noise) dithering and glyph-cell ASCII are per-pixel independent and run as one post pass: divide the screen into 8–12 px cells, sample the cell centre, luma = dot(rgb, vec3(.299,.587,.114)), map luma to a glyph index, draw the glyph either procedurally (Efecto draws a 5×7 bitmap per character in GLSL) or from a canvas-rendered atlas of the site's own typeface. A 64×64 blue-noise tile gives the nicest grain. Error-diffusion dithers (Floyd–Steinberg 7/16-3/16-5/16-1/16, Atkinson distributing only 6/8 of the error for the crunchy Mac look) are sequential and run on the CPU: Efecto renders them in JavaScript on a low-resolution ImageData and lets WebGPU do only the bloom. For a hero that means dither at 360–480 px wide and upscale with image-rendering: pixelated. Reference: Astrodither (glyph cells, pink on black), Efecto (efecto.app, eight ASCII styles, eight dither algorithms), the "Type Dither" Awwwards nominee.

4.10 SVG filters, masks and blend modes — proven

feTurbulence + feDisplacementMap on live text (proven: baseFrequency="0.012 0.03" numOctaves="3" scale="60" on 140 px type) gives a liquid distortion with no WebGL; animate scale or the seed only on entry or hover, because Chrome re-rasterises the filter region on the CPU every frame. mix-blend-mode: difference on 120 px type over a conic gradient inverts cleanly (proven) and is the cheapest way to make type and a canvas belong to each other. A mask-image: radial-gradient(160px at var(--x) var(--y), #000 0, transparent 100%) following the pointer is the Iventions spotlight without a renderer. backdrop-filter works but costs a full re-blur of everything behind per frame, and the glass panel is on the cliché list.

4.11 CSS-only "shaders": @property, linear(), scroll-state()

Registered custom properties animate gradient stops (@property --a { syntax: '<angle>'; inherits: false; initial-value: 0deg }), so conic and radial gradients can breathe with no JS. animation-timing-function: linear(0, 1.2 20%, 0.95 40%, 1) is a spring curve without a library (supported in the lane). @container scroll-state(stuck: top) restyles a sticky header the moment it sticks. All three are compositor-cheap and degrade to the static value.

4.12 Canvas 2D fields (no GPU, no library)

Reaction-diffusion (Gray–Scott) on a 256×256 grid is about 65k cells × 8 iterations per frame of plain arithmetic, which a single JS loop does at 60 fps; write to ImageData, upscale with image-rendering: pixelated. Chladni nodal patterns (cos(nπx)cos(mπy) − cos(mπx)cos(nπy) ≈ 0) need only a few thousand particles that walk toward low-amplitude regions. Both produce the most convincingly "alive" textures on this list and need nothing but a <canvas>.

5. What the render lane can show (verified today)

The lane is Chrome for Testing (Playwright build 1234) with --use-angle=metal on an Apple M1 Max. A probe page (refs/_probe-lane/caps.html, results in caps-meta.json, frame in caps-desktop-top.png) written to document.title and read back from meta.json returned:

| Capability | Result | |---|---| | WebGL 2 | ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Max), GLSL ES 3.00, max texture 16,384, EXT_color_buffer_float yes, anisotropic yes | | WebGPU | adapter apple/metal-3, WGSL pipeline drew; three WebGPURenderer picked the WebGPU backend, forceWebGL picked WebGL | | CSS | animation-timeline: scroll() and view(), startViewTransition, backdrop-filter, mix-blend-mode, anchor positioning, color-mix/oklch, linear() easing, scroll-state(), if(), corner-shape, custom highlights: all true | | Fonts | Google Fonts variable axes load, including from file:// pages | | Module import maps | CDN ESM imports resolve; a file:// page can import jsdelivr modules |

Caveats that change what a screenshot means: prefers-reduced-motion is "no-preference" and colorScheme is forced light; desktop DPR is 1 and mobile 2; there is no user gesture, so audio gates, "click to enter" screens and hover states show their rest state (Astrodither and Unseen needed a scripted click; Active Theory and Ivress preloaders that wait on real time or a gesture never finish); muted video autoplays, unmuted does not; and WebGPU succeeding here says nothing about the Safari-on-iOS-18 visitor, so every WebGPU concept must be rendered once more with forceWebGL: true.

6. Making the mind feel alive without a chat bubble or an orb

The orb fails because it is a body with no behaviour. Presence reads from behaviour: something that notices you, takes time, and settles. Each of these is buildable with sections 4.5–4.12.

  1. A field that grows toward you. Gray–Scott reaction-diffusion at half resolution, seeded at the pointer and at every typed character; the pattern spreads from where attention is and goes quiet elsewhere. Ink on paper, no colour until an answer arrives.
  2. Type that breathes. The lockup set in one variable face whose optical size and weight drift ±5 % on a 7-second noise curve, and whose softness axis rises with pointer proximity. The page is awake before you touch it, and it leans in when you do.
  3. A skin made of the program. The glyph-cell renderer (4.9) drawing a hidden 3D ridge with the site's own words as the glyph atlas: speaker names where it is bright, session times where it is dark. The mind is literally composed of the event's data, and its density is its presence.
  4. An attention vector field. Thousands of short instanced strokes aligned to a field with two attractors: the pointer (400 ms lag) and the composer when it has focus. You can see it looking without a single eye.
  5. Answers that develop, not fade. Components appear through a dither threshold driven from 0 to 1 over 600 ms against a blue-noise map, like a print surfacing in a tray. Removal runs the same curve backwards. One shader pass, no opacity tweens anywhere on the site.
  6. A Chladni plate instead of a face. Particles resting on a nodal pattern whose (m, n) come from a hash of the question; a new question changes the mode and the particles re-settle in about a second. Sound made visible, with no audio permission asked.
  7. Light as the mind. A caustic texture (two layered noises, or feTurbulence animated on entry) swept across the page; sections are lit when relevant to the question and dimmed otherwise. The Iventions spotlight, pointed by the model's ui array.
  8. A latent map of people. 147 speakers as points in a precomputed 2D embedding, drawn as a constellation; an answer pulls the matched ids forward in depth and the rest recede. The "mind map" made literal, and it is the speaker grid component in another costume.

Keep the composer to a single 40–48 px mono line with no box; the field is the interface, the line is the only control.

7. Provocations for ETS27

  1. The whole page is one raymarched SDF mind-scape (soft folds, no head) and every piece of HTML is a mix-blend-mode: difference cut-out in it; the scroll position is a camera uniform fed from scroll(), and the fork is two cut-outs that part when you choose.
  2. Set FREE as a single 400 pt variable-font word whose SOFT and WONK axes are driven by scroll, with counters filled by live reaction-diffusion; YOUR MIND arrives underneath as glyph cells assembling out of session titles.
  3. The agenda is a GPGPU flow field of 68 session particles with four day attractors; hovering a day morphs the field into that day's timeline (four target textures), and clicking a particle develops the session card through a dither threshold.
  4. The lineup is a two-tone blue-noise-dithered contact sheet of 147 portraits, pink on black in one shader pass; the pointer un-dithers a 160 px radius so the only full-colour face on the page is the one you are looking at.
  5. Hold-to-accelerate is the site's one gesture (Astrodither's verb): a countdown to 3 May 2027 runs in the hero, and pressing anywhere fast-forwards the scene through the four days, with the agenda streaming past as the days tick.
  6. No orb, a plate: a Chladni particle field that re-settles for every answer the mind gives; the composer is one 48 px mono line, and answer components rise through the dither reveal while the plate finds its new mode.
  7. The spotlight is the navigation: a cream page where a lavender light cone (one clip-path polygon tweened by GSAP) is the only pointer; ask the mind and the cone swings to the section and un-greys the type it hits.
  8. All motion on print rules: no library eases, only linear() spring curves and threshold reveals, so the site feels developed and printed rather than animated, and reduced-motion is just the final state of each threshold.
  9. Spend the preloader: the loader draws the HUD (speaker count, session count, days) ticking up as the snapshot arrives, in 12 px mono with measurement lines, so the first second already looks like an instrument rather than a spinner.
  10. Mat Voyce logic for the agenda: each session title set in one condensed italic at 160–330 px, stacked and sliding on scroll in a single hue on cream, with the time column the only thing on the page that never moves.

8. Budgets to hold every concept to