Seven movements, zero WebGL. The whole site is DOM, one SVG filter, and a variable font being interrogated in public. Scroll position and scroll velocity are the only two inputs.
Fraunces is a free variable font (Undercase Type, SIL OFL) with four axes — and two of them, SOFT and WONK, are rare enough that most sites never touch them. Kinetic treats the axes as choreography channels, writing font-variation-settings per frame:
The hero letters idle on out-of-phase sine waves across three axes at once, so the wordmark is never twice the same. Movement III scrubs all four axes against scroll and prints the live values like an instrument readout.
0.038). The lag is the physics — no spring library, one line.scaleY; each letter lerps toward its neighbor, making a whip cascade. Volume is conserved with scaleX = 1/√scaleY. Hover springs use one elastic.out tween.wght 100→900, SOFT up a sine arch, WONK 0→1, and opsz 144→9, on acid-against-ink.feTurbulence → feDisplacementMap. Scroll melts the displacement scale in and re-freezes it on the way out; the turbulence frequency itself breathes each frame.clip-path: inset(), shearing apart on an eased scrub. Past 52%, the page inverts to ink. Slice №3 stays coral.|velocity| and whose skewX follows its sign. Stationary readers get gentle weather; violent scrollers get a storm.Bone paper with a multiply-blended noise grain, hairline rules, mono microlabels (Space Mono), display cuts in Fraunces and Syne. Acid is the working accent; coral is spent sparingly — one asterisk, one shard, one number. The custom cursor is a difference-blended dot + trailing ring. Press G for the 12-column grid overlay.
// everything per-frame flows through a single gsap.ticker callback: master(t): lenis.raf(t) // momentum scroll v = lenis.velocity // px/frame, signed heroChars[i].fvs = sines(t, i) // idle wobble inertia.y += (target - y) * 0.038 elastic[i] = lerp(elastic[i], elastic[i-1], 0.32) liquidScale = amt * (46 + 16·sin(1.7t)) marquee.x -= dir · base · (1 + |v|·0.011) · dt cursorRing → lerp toward pointer
One consequence: the site can be driven headlessly — window.__tick(n) forces synchronous frames, which is how every movement was verified without a visible browser.
Full functional audit of all seven mechanisms via forced ticks: axis scrub values, shard clip/shear math, marquee advance, slider playground, overflow scan. Found the preloader could strand in a frozen-rAF tab; wired ScrollTrigger + Lenis + master ticker into one clock.
Added a 4s hard fallback to the preloader, overlap-sealed the fracture slices against subpixel seams, calmed the hero weight oscillation (±95) so stills read as intent, added letter-spacing ooze to the wonk scrub and a blinking scroll cue.
Mobile audit at 375px (zero overflow, no collisions), reduced-motion path (no wobble, no stretch, no cursor), copy re-read, credits table, meta/OG/favicon, console-clean check.
foundry/ ├── index.html # seven <section class="movement"> blocks + one SVG filter ├── css/main.css # palette, movement scaffolding, cursor, grid overlay ├── js/main.js # ~330 lines: one master ticker, ScrollTriggers per movement ├── lib/ # gsap.min.js, ScrollTrigger.min.js, lenis.min.js └── fonts/ # Fraunces VF (roman+italic), Syne VF, Space Mono # the whole trick, in one line: el.style.fontVariationSettings = `'opsz' ${o}, 'wght' ${w}, 'SOFT' ${s}, 'WONK' ${k}`
No build step. No canvas. View source and it's all there.