Plate 201 · Wave IX — Signal · how it was made
A drafting table that rebuilds handwriting from pure rotation.
EPICYCLE is a fictional drafting bureau with one instrument: a canvas-2D table that takes a single stroke of handwriting, computes its discrete Fourier transform, and retraces it with a chain of rotating circles — one circle per frequency, the pen riding the free end. The page has one job: make analysis visible. A terms slider (1–128) shows lossy compression honestly, with the root-mean-square reconstruction error measured live, never scripted.
Palette — argued from the cyanotype
Fourier's France meets the ferro-prussiate print: engineering's blueprint chemistry (Herschel, 1842) turns line drawings Prussian blue, so the signal is cyanotype-white on an iron-blue ground, with one brass instrument accent for the pen and the active spectrum.
Prussian ground — the developed cyanotype plate.
Cyanotype white — LINE/MARK token: the trace, the drafting frame. The signal itself.
Brass — the pointer, spokes, registration marks, lit spectrum terms. Instrument metal, spent sparingly.
Faded plate blue — grid, epicycle circles, the ghost of the original. Never body text.
Body ink, derived from the white and dimmed — 8.4:1 on the ground.
Label ink for HUD keys and meta — 5.4:1, still clear at 10px mono.
Type
Spectral (light, italic for display) carries the bookish French-serif voice of a 19th-century memoir — the headline is set at 300 italic with a rotating circle standing in for the letter o in "sum of circles". Space Mono runs everything instrumental: HUD counters, spectrum captions, the drafting-sheet stamp, tabular numbers throughout.
Techniques
Everything is canvas 2D — no libraries. The preset word "signal" is hand-authored as 37 cubic Bézier segments of connected monoline cursive, sampled and arc-length resampled to 512 points, then ping-pong closed (forward + reverse) to 1,022 samples so the open stroke becomes a closed loop the transform can retrace without a jump sweep. A direct O(N²) DFT (~1.04M complex multiply-adds at N=1022, a few milliseconds, run once per path) yields coefficients sorted by amplitude; the chain keeps the largest M and the pen position is the running complex sum. The pen integrates in fixed sub-steps of τ/1500 so trace resolution is framerate-independent — headless SwiftShader at 5fps draws the same line as a 120Hz display. One lap takes ~11s. RMS error is measured at every slider move over all 1,022 samples and reported as a share of the plate diagonal. The blueprint sheet (grid, grain, frame, stamp) renders once to an offscreen canvas and is blitted per frame; DPR is capped at 2. A state probe (window.__epicycle) exposes terms, RMS, frame count and trace state for QA.
Measured on the preset signature (share of plate diagonal, from the live probe):
| Terms | 2 | 4 | 16 | 64 | 128 |
|---|---|---|---|---|---|
| RMS error | 10.62% | 6.20% | 3.41% | 0.62% | 0.18% |
The error falls monotonically by construction — each added term is an orthogonal component — and the QA harness asserts it, term by term, on both the preset and a programmatically drawn path.
Iteration log
Pass 1 — CraftThe hand-authored cursive preset first read as "eignal": the lowercase s was built as a left-crossing loop and rendered as an e. Rebuilt it three times — left-loop, right-belly Palmer form, finally a printed double-curve with an under-sweep exit — and separated the s/i join, verifying each attempt against a dashed ghost-overlay screenshot of the raw path. The trailing paraph looped like an extra letter; replaced it with an underline swash ending beneath the word. Also fixed the blank-plate state reporting TRACE 100%, and simplified the proof-plate stroke width to 1.3/scale.
Pass 2 — DepthOnce a lap completes, the chain dims to 42% opacity so the parked machine stops obscuring the word it just wrote — the trace stays at full strength. Added the bright leading tail on the last 48 pen points, the G-key ghost overlay of the original stroke, and made the three proof plates re-develop with the visitor's own stroke so section II is personal on a second read. Slider track fills with brass up to the current term count.
Pass 3 — Hardening375px: scrollWidth exactly 375, wordmark and nav inside the viewport, HUD collapses to a 4-cell grid. Reduced motion: no rAF loop; a settled frame renders the full reconstruction with the chain frozen at t = 0.62τ, and the slider still re-renders and re-measures RMS statically. Touch only draws when DRAW is armed (touch-action flips to none) so the page always scrolls. DPR capped at 2, sheet repainted on debounced resize, loop cancelled on document.hidden, brass :focus-visible outlines throughout. Zero console errors across desktop, 375px, and reduced-motion passes.