How Kinetic was made

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.

01 · The instrument: Fraunces

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:

Motionwght 100
Motionwght 900
Motionsoft 100
Motionwonk 1

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.

02 · Seven movements, seven mechanisms

03 · Art direction

bone #EDE6D8
ink #171310
acid #C6F222
coral #FF4F1F

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.

04 · The one ticker

// 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 headlesslywindow.__tick(n) forces synchronous frames, which is how every movement was verified without a visible browser.

05 · Iteration log (three passes)

Pass 1 — correctness

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.

Pass 2 — complexify & harden

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.

Pass 3 — polish

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.

06 · Reproduce it

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.