Guide · How it was made
An infinite world in one fragment shader
Poincaré renders the {7,3} tiling of the hyperbolic plane — infinitely many identical heptagons — inside a single GLSL fullscreen pass, then sails through it on a Möbius flow. The page has one job: make the viewer feel that the shrinking at the rim is a trick of projection, and that the world inside the circle never ends.
Concept
A cinematic single-subject piece in the manner of Escher’s Circle Limit woodcuts, which he cut in 1958 after seeing Coxeter’s figure of this exact tiling. The audience is anyone who has stared at those prints and wondered whether the mathematics is real. It is: every heptagon on the page is congruent in the disk’s own metric, every corner is exactly 120°, and the drift is the genuine isometry group at work — not a zoom, a translation.
Palette
Woodcut black
#141110
The inked block. Page ground and tile fill — warm, not neutral, like oil-based ink.
Paper-cut
#E8DFCB
The uncut paper showing through. Lines and marks only — never body copy.
Ember
#C25F30
The second block of a two-block print. One tile in seven, and the home-tile ring.
Rim-shadow
#5C4E3C
Ink gathering at the horizon. Deep-ring shading, hairlines, the boundary circle.
Body ink
#DCD2BC
Derived from the paper tone for running text — 12.4:1 on woodcut black.
Ember ink
#D97B4C
A lifted ember for small mono accents, 6.1:1 where #C25F30 would fail at text size.
The bright paper tone is reserved for the engraved lines of the tiling; body copy gets its own quieter ink. The accent is argued by the subject — Escher’s two-colour blocks — not by taste alone.
Type
EB Garamond carries the display and the prose: a bookish old-style close to the plate captions in mid-century geometry monographs, set italic for the title so the “é” does some of the art direction. JetBrains Mono runs the instruments — the drift readout, eyebrows, and measurements — because the disk is treated as an apparatus with dials, not an illustration.
Technique
One WebGL2 fullscreen triangle; everything is the fragment shader. Each pixel is mapped into the disk, pushed through the Möbius drift z ↦ (z+a)/(1+āz), then folded into the fundamental triangle of the (2,3,7) triangle group: reflect into a π/7 wedge, invert in a geodesic circle orthogonal to the boundary (radius sin(π/7)/√(cos²(π/7)−sin²(π/3))), repeat until it lands. The heptagon lattice falls out of the folds; strokes are measured in hyperbolic units, so every tile is drawn with the same pen and the rim shrinks them honestly.
The signature problem is tile identity: the fold sequence is recorded as 2-bit opcodes packed into a uvec4 (up to 64 reflections), then replayed in reverse over the origin to recover the heptagon centre of whichever tile the pixel began in. Mapping that centre back through the inverse drift gives a coordinate that is constant in time, so each tile keeps its hash — and its one-in-seven ember colour — while the whole world slides. The drift itself is a breathing translation, ρ(t) and bearing wandering on incommensurate sines, with the cursor allowed to lean on the tiller. The JS shell is discipline only: DPR capped at 2, the loop paused when hidden, a static mid-drift frame for reduced motion, and a CSS woodcut disc if WebGL is lost.
Iteration log
Pass 1 — Craft
Found the WebGL fallback covering a perfectly live canvas: the author rule display:grid was beating the [hidden] attribute, so the CSS disc sat on top of the shader — fixed with an explicit .fallback[hidden]{display:none}. Edge strokes at 0.05 hyperbolic units read as grout, not woodcut; thinned to 0.03. Vertex nodes shrunk from 0.085 to 0.052 units so joints stopped ballooning. Grain amplitude cut by a third. On wide viewports the disk now sits 11% right of centre so the title owns the left column; portrait lifts it 10% clear of the hero copy.
Pass 2 — Depth
The home-tile ring (the tile the drift began on, promised in section III) was technically present but unfindable; it gained an ember centre dot, and the copy’s claim was re-verified against the render. Added the one micro-interaction: pointer position lerps into a small extra Möbius nudge (≤0.055, clamped so |a| ≤ 0.985), so the cursor steers the drift without ever stopping it. Drove the drift headlessly and asserted the travelled-distance integral grows and frames 1.3s apart hash differently.
Pass 3 — Hardening
Removed the background paper-fleck noise — an ornament no one could see (one hash per pixel, zero effect). Re-verified 375px with the header and wordmark inside the viewport box, reduced-motion serving a settled frame at t=11 with the loop never started, context-loss swapping to the CSS disc, resize re-deriving the disk offset, and both pages reading with exactly one h1 and zero console errors.