MOIRÉ

Build notes · Study Nº 195

Making the pattern between the patterns

MOIRÉ is an op-art interference study: a fictional gallery sheet whose single job is to make one law legible — two identical gratings plus one invisible rotation equals one unmissable pattern. The audience is anyone who has cursed a scanned halftone or stood too close to a Bridget Riley.

Concept

The page stages three "stations" of the same law — line-on-line, ring-on-ring, and the dot-lattice twist — cycling on an eight-second clock through one big framed field. One grating of each pair breathes through a slow, eased rotation (or centre-offset, for the rings), and the moiré fringes do all the visible moving. A violet dimension bracket rides the field, live-typing the fringe period D = p / 2·sin(θ/2) as it stretches and collapses: the caption line for the pattern between the patterns.

Palette

Op-art discipline argues near-monochrome: the fringes are luminance phenomena, and any colour laid under them would fight the interference. Gallery white and grating ink carry the whole field; one violet is reserved for measurement marks and small accents — the instrument's ink, never the artwork's; a cool grey handles captions.

#F4F4F2gallery white — the lit wall the gratings hang on
#141414grating ink — every stroke, ring, dot and body glyph
#6B4FD8signal violet — the fringe-caption bracket, tabs, marks
#5A5A60caption grey — eyebrows and wall-label text, 6.2:1

Type

Display & body

Familjen Grotesk

A grotesk with just enough ink-trap character to feel printed rather than rendered — the voice of a 1960s exhibition catalogue set today. Weight 650, tight tracking for display; 400 for body.

Instrument

Space Mono — θ 2.41°

Everything measured speaks Space Mono: readouts, the giant formula, wall labels, the bracket's caption. The moiré is art on one axis and metrology on the other; the mono face is the metrology axis.

Techniques

Pure SVG geometry, no filters, no raster. Each master grating is drawn once in <defs> and instanced twice with <use>: the line comb is a single <path> of 266 vertical strokes (one DOM node, not 266), the bullseye is 120 concentric circles, and the dot lattice is 130 lines wearing stroke-dasharray: 0 13 with round caps — dots as pure stroke geometry, no thirteen thousand circle nodes. Total stage weight stays under ~600 elements, so the browser composites the rotation instead of re-rasterising a pattern tile every frame.

The signature element — fringes sweeping from an invisible cause — is one transform: the second <use> sits in a group whose rotation breathes through a shaped sine (sign(s)·|s|^0.75, periods 15–18 s) so it dwells at the extremes like held breath. The fringe period is computed each frame from the true formula and drives the violet bracket's height and label, so the annotation is measured, not decorative. Moving the pointer across the stage adds a ±0.7° bias to the hidden rotation — the page quietly hands you the grating. Everything pauses on document.hidden, deltas are clamped, and prefers-reduced-motion renders each station settled mid-sweep at its most photogenic angle, tabs still working, nothing blank.

Iteration log

Pass 1 · Craft

  • Drove the full station cycle headless: asserted θ advancing every frame, the auto-cycle reaching station 2 by t≈9 s, and the bracket label re-typing — all live; found the ring station's readout row mutating text nodes through a redundant double-write and cleaned it to a single labelled path.
  • Ring-station fringes read weak at the initial 8.5 px pitch/3.9 stroke — thinned the stroke to 3.6 for a truer 50% duty and the hyperbolas snapped into contrast.
  • Formula block overflowed at 375 px (white-space:nowrap at 6.4vw); rescaled the clamp and let the fraction carry the size instead of the whole line.

Pass 2 · Depth

  • Added the pointer bias — a ±0.7° nudge eased at 4.5%/frame, so the fringes lean toward your hand without ever strobing.
  • Second-read detail: when the breathing rotation falls under one degree, the caption swaps to "the turn is invisible at any zoom — only the bands remember it", then restores.
  • Retimed the three breaths to co-prime periods (18/15/16 s) against the 8 s station clock, so no two visits show the same sequence of rest angles.

Pass 3 · Hardening

  • 375 px: verified no horizontal overflow, wordmark and nav inside the viewport; stage switches to a taller 0.92 aspect with slice so fringes stay monumental on a phone.
  • Reduced-motion: emulated headless — θ holds a fixed 2.4°, two frames hash identical, tabs still switch stations to settled states. CSS media query kills all transitions; the JS gate never starts the rAF loop.
  • Chanel rule: cut a planned second violet annotation (a rotating θ protractor arc in the stage corner) — the bracket alone says it better; removed the readout's redundant write path found in Pass 1.