# Swing type

Huge letters swinging past a fixed window, turning edge-on so the word never assembles. Live at https://design.how/vault#swing-type.

Install the skill, which carries the template, the brief and a check script:

```sh
npx skills add https://design.how/vault/swing-type
```

## What it is

Five huge letters, one flat colour each from a set of five that walk the hue circle at equal chroma and lightness, swing back and forth across a white field. Each letter turns on its own vertical axis as it travels: it squashes to a sliver at the sides and opens to full face in the middle, and past 90 degrees it is gone. The letters sit 78 degrees apart, so a 180 degree window holds 2.3 of them, only two or three ever read at once, and the word never assembles. That withholding is the piece.

The one law is that rotation is proportional to screen x, about 0.44 cap heights per radian, not a cylinder. Each letter is drawn as a parallelogram, an affine transform of a pre-rendered glyph bitmap, so one drawImage reproduces it with no slicing and no WebGL. The swing is a two second pendulum whose amplitude is derived as half the word's angular width, so each extreme lands an end letter dead centre. A detent warps the phase so the row crawls at every multiple of the pitch and clicks from letter to letter instead of whipping past the middle.

The squash is not a cosine alone. Height falls off a measured table every 15 degrees, a little more for tall letters, and the stems lean toward the centre by a second table that peaks near 55 degrees, applied as an in-plane rotation, which is what stops it reading as flat 2D squashing. Letters scale about a point 0.37 caps above the baseline. A heavier cut of the face cross-fades in as a letter turns, so the stem thins more slowly as it squashes.

Every three seconds the word changes, Pivot, Swing, Turns, Angle, all five letters so the geometry never moves. A changing slot takes an extra half turn of its own over 420ms, staggered 90ms left to right, swapping its glyph at the midpoint where it is edge-on and invisible, so the row rewrites itself rather than cutting.

## How it works

- Size the type from the measured cap of the live face (CAP_FRAC of the card height) and rasterise every letter once in two weights into its own bitmap.
- Place letters on a fixed PITCH_DEG angular pitch and swing the whole row with swingDeg(letters) times sin(2 pi t / PERIOD), warped by DETENT at each multiple of the pitch.
- Map each letter's angle to screen x linearly (K_OVER_CAP times the cap per radian) and cull it past CULL_DEG.
- Draw each letter with one drawImage under an affine transform: a horizontal scale by the cosine of its angle, a height from VSCALE, and an in-plane lean from LEAN, about PIVOT_CAP.
- Cross-fade the FONT_WEIGHT_EDGE bitmap in as the letter turns.
- Cue the next word every WORD_SECONDS; flip each slot an extra half turn over FLIP_SECONDS, staggered by FLIP_STAGGER, and swap its glyph at the edge-on midpoint.
- Under reduced motion, draw the frame with the first letter of the first word square-on in the middle.

## What you can change

- `WORDS` (["Pivot", "Swing", "Turns", "Angle"]): The words, cycled. Every word must have the same number of letters.
- `INK` (["#F0413B", "#E88500", "#0FA3A3", "#2F6BE0", "#7A3BD6"]): One colour per letter slot, left to right.
- `BG` (#FFFFFF): The field.
- `PERIOD` (2.0): One full swing, in seconds.
- `DETENT` (0.7): How hard the row clicks from letter to letter. Keep it below 1.
- `CAP_FRAC` (0.46): Cap height as a fraction of the card height.
- `WORD_SECONDS` (3): How long each word holds before the next.
- `FONT_VAR` (--visual-sans): The CSS variable holding the font family. A plain grotesque with a heavier cut works.

Last updated: 2026-11-02
