Skip to Content

Railway Clock

Interaction · Nov 2026

The Swiss station clock rebuilt in the browser, with an X-ray of its works: a mains motor that never stops, a slipping clutch, a catch that stops a pin at 12, a polarized armature that flips on alternating minute pulses, gears that mesh by real ratios, and every part you can turn by hand.

The Idea

The red hand goes round a little fast, stops at 12 and waits. When the minute changes it sets off again, and the minute hand steps forward one mark on the same signal.

The clock opens in X-ray: a lens shows the works behind the dial, laid out the way Swiss station slave clocks work and drawn larger than life. Every part moves from the same clock that drives the hands, so what you see is always what moves the face. You can grab any gear and turn it, and you can press the catch yourself.

Threads leaves a thread from the tip of the second hand every second. Wind the Threads winds each finished minute onto a spool, one wrap a minute, until the hour is full and the spool lets go.

Where It Comes From

Hans Hilfiker designed the clock for Swiss railway stations in 1944 and added the red second hand, after the signal disc railway staff used to send trains off, in 1953. The second hand goes round in about 58.5 seconds, waits at 12 for about 1.5, and restarts on the master clock’s minute impulse (Wikipedia).

A station clock is a slave clock: it cannot keep time on its own. The master clock sends one polarized pulse a minute, at 12 to 60 V DC, and that pulse moves the minute hand one step. The second hand has its own synchronous motor on the 230 V, 50 Hz mains (Bürk Mobatime NU 90/SYN). That motor runs all the time. At 12 a lever stops a pin on the second wheel, a slipping clutch lets the motor turn on underneath, and the minute pulse lifts the lever (DG-Chrono forum). Running early and waiting means the motor’s small errors never add up: every lap ends at 12, on the master’s time (Mobatime).

The X-ray follows that arrangement. The layout, the tooth counts and the shapes are mine. The drawing style borrows from Teenage Engineering: light grey plastic, fine black lines and one orange. The threads came from Zizi’s string clock on quynh.moe.

Why It Matters in an Interface

Several things finish at different times but belong together: cards loading from different sources, a count shown on two screens. The clock’s answer is to run a little early and wait for one signal, so nothing can arrive late, and to correct itself on every lap without anyone measuring the error. Let each part get ready, hold it, and change everything on one signal.

The X-ray shows the cause behind the motion without cluttering the face for everyone else. A lens lets the curious look inside one place at a time, and the chips under X-ray hide parts so you can follow one piece at a time.

How It Works

One clock for everything

Everything runs from the time since the minute began, s, and the minute itself. The minute signal is the UTC minute changing on the device’s clock, so every open clock changes on the same frame. The second hand is one function of s:

railway-clock.html

const ROUND = 58.5;
const RAMP_IN = 0.03;
const RAMP_OUT = 0.04;
const SPEED = 360 / (ROUND - RAMP_IN / 2 - RAMP_OUT / 2);
function sweep(s) {
  if (s < RAMP_IN) return (0.5 * SPEED * s * s) / RAMP_IN;
  if (s < ROUND - RAMP_OUT) return SPEED * (s - RAMP_IN / 2);
  if (s < ROUND) {
    const u = ROUND - s;
    return 360 - (0.5 * SPEED * u * u) / RAMP_OUT;
  }
  return 360;
}

SPEED comes to about 6.16 degrees a second. The ramps last 30 and 40 milliseconds, a frame or two, because the motor is already turning when the hand is let go and the hand stops dead against the catch. The minute hand steps on a spring with a stiffness of 260 and a damping ratio of 0.7, so it overshoots about 5% of a step and settles. The hour hand turns at a twelfth of the minute’s angle, half a degree a minute.

The gear train

The motor drives the second wheel through two stages. Its 8-leaf pinion turns a 24-tooth middle wheel (3 to 1), and the middle wheel’s 8-leaf pinion turns the 72-tooth second wheel (9 to 1), so the motor turns 27 times a lap. Every radius in the drawing is a pitch circle, where two meshing gears touch, and the train shares one module (pitch diameter over teeth) of 2/3. So the second wheel’s pitch radius is 24, each pinion’s 8/3 and the middle wheel’s 8, and meshing gears sit exactly the sum of their pitch radii apart: 26.67 from the second wheel to the middle arbor, 10.67 from there to the motor.

The clutch sits between the middle wheel and its pinion. The wheel turns with the motor, always; the pinion turns with the second wheel and slips while the hand is held:

railway-clock.html

m.motorA = (m.motorA + 27 * SPEED * dt) % 360;
turn(m.rotor, m.motorA, WORK.motor.x, WORK.motor.y);
turn(m.middle, -m.motorA / 3 + m.phase.midWheel, WORK.middle.x, WORK.middle.y);
turn(m.middlePin, -9 * S + m.phase.midPin, WORK.middle.x, WORK.middle.y);
turn(m.ratchet, c.minute.p, 100, 100);

S is the second hand’s angle. During the wait at 12 the middle wheel keeps turning and its pinion stands still, as the real one does.

Clock teeth

Clock gears are traditionally cut with a cycloidal profile rather than the involute of machine gears (Wikipedia): radial flanks below the pitch circle and a rounded tip above it. Each gear here is drawn that way, as one SVG path, from this table:

railway-clock.html

const GEAR = { wheel: { add: 1.25, ded: 0.95, thick: 0.47 }, pinion: { add: 0.65, ded: 1.55, thick: 0.38 } };

A wheel’s teeth stand 1.25 modules above the pitch circle and are 0.47 of a pitch thick there. A pinion’s leaves are short (0.65) and thin (0.38), with deep gaps (1.55 below the pitch circle). A wheel tooth goes 1.25 modules into a gap 1.55 deep, and a leaf 0.65 into a gap 0.95 deep, so each has 0.3 of a module to spare. Each tooth is a radial line up to the pitch circle, two cubic curves over the tip, a radial line down, and an arc along the root to the next tooth.

Meshing in phase

Two gears of the right sizes still collide if a tooth meets a tooth. Each driven gear B turns at -(nA / nB) times its driver A, plus a fixed phase chosen so that whenever one of A’s teeth lies on the line between the centres, one of B’s gaps lies there too. Teeth sit on whole pitches and gaps on half pitches, which gives:

railway-clock.html

function meshPhase([ax, ay], nA, [bx, by], nB) {
  const ab = (Math.atan2(bx - ax, -(by - ay)) * 180) / Math.PI;
  return ab + 180 + (nA / nB) * ab - 0.5 * (360 / nB);
}

ab is the direction from A to B, measured clockwise from 12 as the dial is. Once in phase, the ratio keeps them locked. Gears that mesh on one level are drawn backs first and faces second, so the extruded side of one never covers the face of the other.

The catch

The catch is a lever on a pivot right of the coil. Its arm lies along a line tangent to a circle of radius 26.5 around the centre, clear of the second wheel’s tooth tips at 24.83, and a hook at its end reaches down to radius 19.8, across the path of a pin standing on the wheel at radius 20.5. The tangent point comes from the pivot alone:

railway-clock.html

const R0 = Math.hypot(dx, dy);
const phi = Math.atan2(dx, -dy); // the pivot's own angle round the dial
const a = phi - Math.acos(line / R0);

Lying along the rim means a small turn of the lever moves the hook almost straight out from the centre, so it lifts clear of the pin instead of sliding along it. A pad of soft iron hangs off the hub 0.35 from the end of the coil’s core. Soft iron is pulled by either polarity, so every pulse closes that gap, which turns the lever 6.7 degrees and lifts the hook about 2, clear of the pin. A blade spring under the arm holds the pad back against a stop pin the rest of the time. The pulse is held 1.4 seconds, long enough for the pin to pass under the hook, about 7.5 degrees of travel.

Running early and catching up

Press the catch while the hand waits and the pin slips by. From then the hand runs ahead, and the clock carries that lead until the catch takes it back:

railway-clock.html

if (c.catchHeld && s >= ROUND) c.free = true;
if (signal) c.free = false;
if (s >= ROUND && c.free) c.ahead += SPEED * dt;
else if (s >= ROUND) c.ahead = 0; // waiting at 12 in step
let hand = base + c.ahead;
c.caughtEarly = false;
if (c.ahead > 0 && !c.catchHeld && s < ROUND && hand >= 360) {
  c.ahead = Math.max(0, 360 - base);
  hand = 360;
  c.caughtEarly = true;
}

base is sweep(s). On the next lap the hand reaches 12 early and is held there, and its lead shrinks as true time catches up, to nothing by 58.5 seconds. The minute signal then lets it go exactly in step. That is why the real clock runs fast and waits: it cannot be late, and any lead is cancelled at 12.

The minute magnet

The armature is a permanent magnet laid as a seesaw on a post over the coil, between two pole shoes on the core’s ends. A plus pulse pulls one end down onto its shoe, a minus pulse the other, and it stays where it lands, with no return spring. The pulse alternates with the minute:

railway-clock.html

m.pol = utcMin % 2 === 0 ? 1 : -1;

The seesaw tips 2.35 degrees either way and stops hard on each shoe. An arm from its pivot carries an anchor with two pallets into the 60-tooth ratchet on the minute arbor, so each flip, either way, moves the ratchet one tooth: one minute. The leads and the coil light while the pulse is on, and small plus and minus marks beside the coil swap each minute.

The motion works

Under the dial, the cannon pinion (10 leaves) on the minute arbor turns a minute wheel (30), and that wheel’s pinion (8) turns the hour wheel (32): 3 times 4 is 12. They use a module of 0.2 and the same tooth profile and phasing.

Drawing it

The works are SVG, built once in code, in a group scaled 1.7 times about the centre. Each part is drawn twice, its side offset 0.85 right and 1.2 down and then its face, joined by short lines at its silhouette, which gives the flat, extruded look. Each moving part is one group with one rotate() a frame. The lens is a clip path circle: 40 under a mouse, 54 under a finger (held 48 above it, so the finger never covers what it shows) and 118 to open the whole dial. Inside it the works are magnified by 1 + 0.4 × min(1, max(0, (100 - r) / 60)), so a small lens shows more detail and the open dial is life size.

Sound

Every sound is made in the browser with Web Audio, with no files. Each part is struck: 5 milliseconds of noise through a 1.5 kHz high-pass for the hit, then a ring of two sines (the part’s pitch and 2.76 times it, as a struck bar rings) dying away in tens of milliseconds, with a low sine under the heavier parts. The gears tick as their teeth meet. The motor’s pinion meets the middle wheel every 45 degrees of the motor, about 3.7 times a second, and never stops; each tooth of the second wheel passes about 1.2 times a second and falls silent while the clutch slips at 12:

railway-clock.html

const motorTooth = Math.floor(m.motorA / 45);
const secondTooth = Math.floor(S / (360 / WORK.second.n));

Each tick varies a little in pitch and level. They peak between -13 and -9 dBFS, near the site’s own sounds.

Playing with it

Press any gear and turn it. The pointer is mapped into the works’ own coordinates through the group’s screen transform, so it lands true through the lens’s magnification. Each gear knows its ratio to the hands: the second wheel 1 to the red hand, the middle wheel and pinion -1/9, the motor 1/27; the ratchet and cannon pinion 1 to the minute hand, the minute wheel -3 and the hour wheel 12. Let go and the hands spring back to the true time, carrying the speed of your throw. Press the catch and it lifts as the pulse lifts it, with the pad’s click.

Threads and the spool

Each second a thread is pinned at radius 80 and hangs to the tip of the hand on a spring. At the minute the threads fall away under gravity or, with Wind the Threads on, wind onto the spool, the far ones first, adding one wrap. On the hour the spool is full at sixty and unspools, one wrap every 32 milliseconds, each drifting off over a second and a half. Skip to the Hour sets the clock seven seconds before the hour so you can see it.

Night Light on Auto follows your dark mode. Every option is saved, shared between open tabs, and can be set in the link: ?xray=1&wind=1&hide=hands,threads opens the X-ray with the hands and threads hidden.