e·nig·ma
A scroll-driven teardown of the Enigma I on a night watch in Hut 6, from oak lid to reflector, ending in a faithful machine you can type on.
Open the teardown →
- StackThree.js + TypeScript
- AssetsThree GLBs, all scripted
- Code≈2,500 lines
- Shipped27 Aug · hut 7 Sep 2026
An Adapt Progress Evolve Limited project by Jack Stovell (registered in England and Wales, company no. 10047328, VAT GB362654684) · Source: github.com/StovBuilds/jarvising · src/enigma/
What it does
You scroll, and the machine comes apart. Nine chapters run from the hero orbit through a timeline of Scherbius's patent, then the camera leans in to read the card inside the lid, the oak case lifts into six labelled layers, the middle rotor slides offstage and explodes into its own stack, one keypress is traced as glowing current through plug board, rotors and reflector and back, and the rotors step like an odometer.
Then the room takes over. The machine sits on a desk in Hut 6 at Bletchley Park, on the night watch: one pendant lamp, a blackout window, a notice board with the day's crib pinned to it. Two chapters tell the story of the people who broke it, from Rejewski's paper reconstruction in 1932 and the Pyry hand-over of July 1939 to Turing and Welchman's bombes and the thirty-year silence that followed.
The last beat is a working Enigma I: rotors I·II·III, reflector B, ten plug pairs, the double-step anomaly included. Type on your keyboard or tap the keys; the lamps answer and a paper tape keeps both alphabets. "Copy secret link" bundles the start position and ciphertext into a URL, and because Enigma is reciprocal, whoever opens it watches the machine type the plaintext back out.
Sound is opt-in and synthesised live: key clacks, lamp thunks, rotor ratchets, a lid creak, and faint Morse under the hero. No audio files either.
Etymology
First attested as experiment 008 on jstov.uk's lab on 27 August 2026; moved here on 7 September and grown up in a day. Below, the build replays itself: every frame is the piece rebuilt from git at that commit and photographed at the same point in its scroll, so the only thing that changes is the work.
Enigma machine parallax — what it did / when created / how it works piece by piece.
The session's summary of Jack's ask, from the state log entry that opened the work on 27 August 2026. Not his exact words.
- Logged to first commit29 min
- First commit to live8 min
- Logged to live36 min
Day one, from the records: the commitment ledger, git log on jstov (6054eaf, then a lint fix) and the commits that wrote the state log. The state log's own headings were typed by hand, so the durations are measured on the machine clocks. First commit to live: eight minutes.
The latest frame, 7 Sep 2026. With JavaScript on, drag across the picture or use the slider to replay all eight.
6054eafjstov: 008 — ENIGMA, a scroll-driven teardown8736c28jstov: enigma polish — sound, live traces, shareable secrets, grade164c3d0Ported to jarvising.com as entry 001ce3614fHut 6 at night, anchored callouts, lid card, Bletchley chapters734e5bbThe bombe, a Blender-built GLBe442ad6The Enigma itself from a Blender parts library46aba59Engraved key letters, running bombe, hut props9210418Rotor solo: the machine lifts offstage
Frames rendered by tools/render-build-frames.mjs in software WebGL, pinned at ?p=0.31 (the assembly) and ?p=0.45 (the rotor). The two jstov frames come from that commit's own Enigma files mounted alone, without the rest of jstov.uk; the list gives each commit's subject shortened, the caption above gives it in full.
How it was made
Everything is generated at runtime. The oak grain, the crinkle-paint chassis, the alphabet rings, the key caps, the painted hut boards, the floorboards, the notice board and the card inside the lid are all 2D canvases drawn on the fly and used as textures. The machine is built from primitives as named parts, each carrying a resting position, a full explode vector and a small lag, so one scalar can pull the whole thing apart in a staggered order. The room is a second set of primitives around it, lit by one shadow-casting spotlight where the pendant bulb hangs.
One number drives the page. Scroll position becomes a 0–1 progress value, smoothed per frame. Every effect is a smoothstep window on that value: the explode envelope, the lid hinge, the rotor solo, the two current tubes revealed by advancing their draw range, the camera path sampled from ten orbit keyframes, the chapter copy, the callout fade-ins. Nothing is on a timer, so scrubbing backwards just works.
The labels are 3D points. Each callout is an invisible object parented to the actual mesh it describes, so it rides along through the explode, the lid hinge and the rotor spin. Every frame it is projected through the camera, placed to the near side, clamped inside the viewport and away from the copy, pushed clear of any label it would overlap, and drawn as HTML with an SVG leader line back to the point.
The machine's parts come from Blender now. The case with its rim moulding, corner plates, hinges and latch; the lid with its inner frame; the riveted deck and plates; a dished, ringed key cap; a knurled lamp bezel; a twin-jack plug socket; the serrated thumb wheel, alphabet ring, pinned core and contact plate of each rotor. Each is one mesh in a small glTF parts library, authored by a Python script in the same local frame as the primitive it replaces, so the page keeps every position, hinge, explode vector and callout anchor and simply swaps geometry in. If the file does not arrive within a few seconds, the primitives build as before.
The bombe runs. Chapter 008 needed a machine to look at, and primitives in the browser were never going to make 108 drums read. It is built the same way, welded and quantised to under a megabyte, and loaded lazily once you are past the rotor chapter. Its drums are exported as GPU instances rather than one merged mesh, so each can turn on its own axis: while the hut chapters play, the top drum of every stack spins, the middle one steps once per revolution, and the bottom one waits, which is how a bombe actually ran. Drum colours follow the period convention of one colour per rotor type.
The key letters are geometry. Twenty-six letters converted from text in Blender stand four millimetres proud of the caps, in ivory. The lamp letters stay as textures because they have to glow. The hut's dressing, a Bakelite telephone, a Typex-style machine on the second desk, a pot-belly stove and a greatcoat on a hook, comes from a third small library.
The cipher is separate from the picture. An 80-line class holds the historical wirings and does the encryption; the scene only asks it which lamp lit. The live current trace reuses the same path factory the authored chapter uses, evaluated at an explode value of zero.
Built with Claude Code from a one-line brief, in the same session that shipped it as experiment 008 on jstov.uk's lab. This is now the primary version: the hut, the lid card, the anchored callouts and the two Bletchley chapters were added here first.
How to make your own
- Write the mechanism first, with no graphics. A pure function you can test against a known vector. For Enigma: at AAA with no plugs, pressing A five times gives BDZGO.
- Model in named parts, not one mesh. Give every part a base position, an explode direction and a lag. The teardown is then
base + dir × clamp(e × (1 + lag) − lag)for one scalare. - Derive everything from one progress value. Read scroll, normalise to 0–1, ease it a little each frame, and express each beat as a smoothstep between two progress values. Never start a timer.
- Keyframe the camera in spherical coordinates. A short list of
{p, theta, phi, r, target}entries, interpolated by progress, reads far better than any free-fly control. - Put labels in 3D and project them. Anchor to a part, call
project(), position an HTML element, draw the leader line in one SVG you rewrite per frame. - Keep two timelines. Scroll position maps to a "beat" value through one function, so new chapters can be inserted by remapping instead of retuning every window. The Bletchley chapters hold the machine on its stepping beat while the camera pulls back.
- Add a debug parameter that pins progress.
?p=0.42lets you screenshot any beat on a machine with software GL, where smoothed values never settle. - Reach for Blender when primitives stop reading. Keep it scripted so the asset is reproducible, keep the scene scale identical, and load it lazily so the first paint stays light. Skip Draco and meshopt if your page ships a strict content-security policy; quantisation alone halves the file and needs no decoder.
- Replace geometry, not structure. Ship a parts library named after your scene graph, authored centred on the same local origins, and keep your positions and animation in code. One catch: if you pull quantised geometry out of its node, convert positions to float first, or anything larger than a unit gets clamped flat.
- Make sound opt-in and synthesised. Create the AudioContext on the user's click, and build clacks and ratchets from filtered noise bursts. Zero downloads, and no autoplay policy to fight.
The whole cipher, for the taking (the full piece is in src/enigma/ on GitHub):
// Enigma I: rotors I·II·III, reflector B. Historical wirings, public domain. const WIRINGS = ["EKMFLGDQVZNTOWYHXUSPAIBRCJ", "AJDKSIRUXBLHWTMCQGZNPYFVOE", "BDFHJLCPRTXVZNYEIWGAKMUSQO"]; const NOTCHES = [16, 4, 21]; // Q, E, V const REFLECTOR = "YRUHQSLDPXNGOKMIEBFZCWVJAT"; const mod = (n) => ((n % 26) + 26) % 26; const fwd = WIRINGS.map((w) => [...w].map((c) => c.charCodeAt(0) - 65)); const rev = fwd.map((f) => { const r = []; f.forEach((v, i) => (r[v] = i)); return r; }); const ref = [...REFLECTOR].map((c) => c.charCodeAt(0) - 65); let pos = [0, 0, 0]; // left → right, 0 = 'A' in the window function step() { // includes the double-step anomaly if (pos[1] === NOTCHES[1]) { pos[0] = mod(pos[0] + 1); pos[1] = mod(pos[1] + 1); } else if (pos[2] === NOTCHES[2]) pos[1] = mod(pos[1] + 1); pos[2] = mod(pos[2] + 1); } function press(letter, plug = new Map()) { let x = letter.toUpperCase().charCodeAt(0) - 65; step(); x = plug.get(x) ?? x; for (let i = 2; i >= 0; i--) x = mod(fwd[i][mod(x + pos[i])] - pos[i]); x = ref[x]; for (let i = 0; i <= 2; i++) x = mod(rev[i][mod(x + pos[i])] - pos[i]); x = plug.get(x) ?? x; return String.fromCharCode(65 + x); }
Plug pairs go in the map both ways. Reset pos to the same start position to decrypt.
History notes
Things the chapters touch but cannot dwell on, each checked against the sources below.
- The Herivel tip and cillies, May 1940. John Herivel guessed that a tired operator setting up in the morning would leave the rotors near the ring-setting he had just fixed; with operating slips nicknamed "cillies", that guess carried Hut 6 for months before enough bombes existed.
- Banburismus, Hut 8. Turing's paper method for naval Enigma, named for the punched sheets printed in Banbury, cut the bombe work by ruling out wheel orders statistically.
- U-110, 9 May 1941. HMS Bulldog's boarding party took an intact naval Enigma and its key documents from a sinking U-boat. The King called it perhaps the most important single event in the whole war at sea.
- Shark, 1942. On 1 February 1942 the U-boats switched to the four-rotor M4 and Hut 8 went blind for ten months. On 30 October, Lieutenant Tony Fasson and Able Seaman Colin Grazier of HMS Petard, with canteen assistant Tommy Brown, went into the sinking U-559 for its weather code book; Fasson and Grazier did not come out. Shark was read again from 13 December.
- Matapan, March 1941. Mavis Lever, later Batey, working under Dilly Knox on Italian naval Enigma, broke the message that put the Italian fleet at sea; the Royal Navy met it off Cape Matapan on 28 and 29 March.
- What a hut actually did. Intercepts arrived in Hut 6's Registration Room, went to the Machine Room to be attacked, then to the Decoding Room to be typed out on modified British machines, then through a wooden tunnel to Hut 3, on a tea-tray pushed with a broom handle, to be translated and assessed.
The lid card in the piece keeps the real plate's heading, Zur Beachtung!, but the original carried maintenance instructions; ours explains the machine instead. The notice-board cards and the intercept form are evocations, not transcriptions.
Sources
- GCHQ, The Pyry Forest meeting and GCHQ, Mavis Batey
- Bletchley Park Trust, Hut 3 and Hut 6; Inside Hut 6; Shark Attack; The women of Bletchley Park
- The National Museum of Computing, the Turing-Welchman Bombe and its historical background
- Crypto Museum, Enigma history and Enigma I
- uboat.net, Allied breaking of naval Enigma; naval-history.net, the capture of U-110; Royal Museums Greenwich, HMS Petard
- F. H. Hinsley, The Influence of ULTRA in the Second World War (1993 lecture)
- Books: Hinsley & Stripp, Codebreakers; Welchman, The Hut Six Story; Copeland, The Essential Turing; Sebag-Montefiore, Enigma: The Battle for the Code; Winterbotham, The Ultra Secret (1974)
A claim-by-claim ledger with verdicts lives in the repo: docs/enigma-sources.md.
See also
Entry 002, Cortex: a fleet’s memory as a living 3D war table, handed on as a library.
Entry 003, Inside the Rig: the same teardown idea at the scale of a whole gaming PC.
Entry 004, Below the Layer: the same war at sea from under the water, a British submarine hiding from an escort’s sonar.