Guide — how this machine is built

Velocity

A kinetic typography engine for Nomadic Owls. No photography, no illustration, no 3D — the variable font is the entire asset library. Motion is the identity; the grid is the discipline that makes it legible.

01 — The concept

Type is the only cargo

 

Nomadic Owls sends beautiful things to the Internet. VELOCITY takes that sentence literally: the site is a dispatch terminal where words themselves are the freight. Services depart from a split-flap board. Projects ship as typographic manifests. The contact line is an outbound channel.

Every kinetic system obeys one law: chaos in transit, order at rest. Glyphs may stretch, flip, scatter and gain mass while moving — but the moment motion stops, everything settles into strictly gridded, perfectly legible Swiss typography. If a visitor never scrolls, they still get a beautiful poster.

02 — Design system

Palette · type · axes

 

Palette — three colors and a bone

Bricolage Grotesque — the three axes

wght 200 – 800 Weight. Scroll velocity pushes it toward 800; rest settles at 620.
wdth 75 – 100 Width. Speed compresses glyphs to 75 — the typographic equivalent of motion blur.
opsz 12 – 96 Optical size. Display set at 96 for tight apertures; prose at 24 for open counters.

Live specimen — drive the axes yourself

Beautiful things, sent fast

Numerals and labels are Spline Sans Mono 300–500 — the fixed-pitch counterpoint that makes the grotesque feel fast.

03 — Tech & motion

Three engines, one rAF heart

 

A — The velocity engine

One requestAnimationFrame loop measures scroll displacement per millisecond. The raw signal is asymmetrically smoothed — it rises fast (k = 0.16) and decays slow (k = 0.045), so type snaps into motion and settles like a needle. The normalized value maps onto the font axes of every letter in the headline, phase-shifted per letter to ripple like a wave through the line.

raw    = (scrollY − lastY) / dt              // px per ms
smooth = lerp(smooth, |raw|, raw > smooth ? 0.16 : 0.045)
norm   = clamp(smooth / 2.6, 0, 1)           // 2.6 px/ms ≈ flat out

// per letter i — a travelling wave keeps it organic:
phase  = 0.75 + 0.25 · sin(t·0.004 + i·0.9)
wght   = 620 + 180 · norm · phase            // mass increases
wdth   =  94 −  19 · norm · phase            // width compresses

B — Split-flap board mechanics

Each character cell owns a position on a drum:  A–Z −. When a row enters the viewport, every cell starts flipping at a fixed mechanical tick of 42 ms, offset by 26 ms per column and 140 ms per row. A cell's start position is derived backwards from its target letter, so flip counts vary 6–15 — cells land at different moments, exactly like a real departures board. Every tick collapses the glyph vertically (scaleY 1 → 0.35 → 1) via the Web Animations API to fake the flap without images.

startIdx = (targetIdx − flips) mod drum.length
every 42ms:  cell.text = drum[(startIdx + step) % drum.length]
             cell.animate(scaleY: [1, 0.35, 1], 42ms, linear)
stop when step = distance(startIdx → targetIdx)

C — Inertia marquee physics

The bands integrate three forces per frame: a constant brand-drift, a coupling term fed by page scroll velocity (the whole site shares one bloodstream), and thrown momentum from pointer flicks, decaying with exponential friction. Position wraps modulo one chunk width, so the band is infinite in both directions.

if grabbed:   pos += Δpointer;  vel = smooth(Δpointer)
on release:   momentum = clamp(vel, −60, 60)
each frame:   pos −= dir · base · (1 + scrollNorm · gain) · dt
              pos += momentum;   momentum ×= 0.94
wrap:         pos = pos mod chunkWidth

D — Scatter, magnetism, restraint

Section titles are split into letters at build time (real spans, zero CLS, screen-reader copy preserved). Each letter carries a deterministic scatter transform seeded from its index; an IntersectionObserver flips one class and CSS transitions — cubic-bezier(0.16, 1, 0.3, 1), 28 ms stagger — reassemble the line. The contact CTA measures pointer distance per letter and converts proximity into weight and displacement through a smoothstep curve. And when prefers-reduced-motion is set, every engine stays off: the site renders as a finished, static, fully legible poster.

04 — How to reproduce

Build one yourself

 

  1. Stack. Astro 5 static output, zero client frameworks. Self-hosted fonts via @fontsource-variable/bricolage-grotesque (standard.css = all three axes) and @fontsource/spline-sans-mono. No CDNs, no runtime Google Fonts, no GSAP — the whole motion stack is ~6 KB of vanilla ES modules.
  2. One ticker. Build a single shared rAF loop with subscribe/unsubscribe. Every system — velocity engine, marquees, magnetic letters, HUD — is a subscriber. The loop stops itself when nothing is subscribed.
  3. Split at build time. Letter spans are generated in the Astro component, aria-hidden, with an .sr-only duplicate of the full string. Never split text in the browser; never draw content into canvas.
  4. Axes, not transforms. The speed effect lives in font-variation-settings — real glyph geometry changing, not scaled rectangles. That is why it feels typographic instead of gimmicky.
  5. Settle. Tune every decay constant until the interface lands quietly. The kinetic layer must be removable: check the reduced-motion rendering first, then add speed back on top.

Credit

Designed & built by Claude (Anthropic) for Nomadic Owls. One of ten directions — see the other nine at nomadic-ten.pages.dev ↗

No AI imagery used — the variable font is the asset. Everything on these pages is text, math, and three colors.