Everybody has seen the video: a tile the size of a grain of rice goes over, and thirteen dominoes later something the size of a door does. The number attached to it is half again — each domino can knock over one about 1.5× its own size — and it is quoted as though it were a law. It is not. It is a working number, several per cent under a ceiling that is set by geometry, and the interesting thing about building one of these is not the growth factor at all.
Three numbers, and the one everybody quotes is not the ceiling
A domino of height h and thickness t has to be lifted onto its own corner
before gravity will take it the rest of the way. Both energies are pure
geometry:
barrier = m·g·(√(h²+t²) − h) / 2 standing → balanced
release = m·g·(√(h²+t²) − t) / 2 balanced → flat
Mass goes as h³ and the lever as h, so both scale as h⁴ and their ratio
does not scale at all:
| height | mass | barrier to balance | released after it | ratio |
|---|---|---|---|---|
| 6 mm | 0.0194 g | 6.90e-9 J | 4.88e-7 J | 70.8 |
| 48 mm | 9.92 g | 2.82e-5 J | 2.00e-3 J | 70.8 |
| 1000 mm | 89.7 kg | 5.32 J | 377 J | 70.8 |
One domino carries 70.8× the energy needed to start the next one, across
five decades of size, and since energy goes as h⁴ that licenses a growth
factor of 70.8^(1/4) = 2.90×. Every domino could topple one nearly three
times its height, if the energy could be delivered.
It cannot, and src/half-again.mjs is there to say by how much. Each domino is
one degree of freedom — an angle about its own front bottom edge — with
inelastic contact and Coulomb friction between the falling corner and the face
it slides up. That model buys two exact facts, and they are the whole piece:
a normal push on the back face, τ up from the floor,
has moment arm exactly τ about the pivot → hit low, get nothing
the falling domino's corner pushes with moment arm
h₁·cos(a₁ − a₂) about its own pivot → and the two must agree
The second number is the measured one: 1.56×, at the best spacing there is. Folklore’s 1.5 is not the ceiling. It is about four per cent under it, which turns out to be exactly the right place to be.
The chain has no memory, and that is not a metaphor
A row where every domino is g times the last and every gap is λ times the
falling one is scale-invariant: scale the picture by g and you have the same
row, one step along. So the only honest way to read the arrival speed is the one
form that survives that scaling, ŵ = ω·√(h/g) — and in that form a row has a
fixed point it goes to no matter how it was started.
| flick ŵ | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | tail |
|---|---|---|---|---|---|---|---|---|---|
| 0.25 | 0.48 | 0.76 | 0.97 | 1.08 | 1.13 | 1.16 | 1.17 | 1.17 | 1.172 |
| 1.20 | 1.27 | 0.95 | 1.03 | 1.10 | 1.15 | 1.16 | 1.17 | 1.18 | 1.173 |
| 16.00 | 16.01 | 7.55 | 4.87 | 3.13 | 2.15 | 1.61 | 1.36 | 1.25 | 1.183 |
A flick carrying 4000× the kinetic energy of another is gone in six dominoes. You cannot shove a chain into working. Every step pays for itself out of its own fall, which is why the demo’s camera zooms with the wave — at constant growth the view is identical at every step, because the physics is.
The demo makes this the main view on purpose. The thing people expect to see is a chain getting more impressive; what you actually watch is the same event fourteen times, with the ruler underneath changing units.
Spacing has two optima and they are at opposite ends
| λ = gap/h | pitch | arrival ŵ* | wave speed | resting angles |
|---|---|---|---|---|
| 0.10 | 12.3 mm | 0.349 | 0.58 m/s | 59/61/63/66/69/75° |
| 0.20 | 17.1 mm | 0.706 | 0.72 m/s | 65/65/66/67/68/70° |
| 0.30 | 21.9 mm | 0.922 | 0.75 m/s | 70/70/70/70/71/71° |
| 0.50 | 31.5 mm | 1.174 | 0.72 m/s | 76/76/76/76/76/76° |
| 0.70 | 41.1 mm | 1.313 | 0.66 m/s | 79/79/79/79/79/79° |
| 0.90 | 50.7 mm | 1.342 | 0.54 m/s | 90/90/90/90/90/90° |
Two things worth stopping on. The wave speed peaks around λ = 0.30 at 0.75 m/s — in the right neighbourhood for a real 48 mm domino — while the growth ceiling, below, peaks at λ = 0.96. Fastest and strongest are not the same spacing and they are not close.
And the resting angles, which are the tell. I expected a tight row to stall and
it never does: it goes down leaning, every domino propped on the one in
front, finishing in the sixties rather than flat. The push is still travelling;
it is just arriving from a domino that has barely started to fall. That has an
exact bound. A domino can lean at most asin(λ) before its corner lands on the
next face, and it needs atan(t/h) = 8.87° just to pass its own balance point:
| λ | asin(λ) | measured lean at first touch | past balance by then? |
|---|---|---|---|
| 0.10 | 5.74° | 5.86° | no |
| 0.15 | 8.63° | 8.77° | no |
| 0.20 | 11.54° | 11.68° | yes |
| 0.70 | 44.43° | 44.43° | yes |
The simulator finds that limit to within 0.24° without being told it, which is
the check I trust most in the whole file — and the error is one-sided, always a
fraction of a degree past asin(λ), because contact is detected at the end
of a timestep rather than during one. Below sin(8.87°) = 0.154 no
domino in the row can reach its own balance point while its neighbour stands —
the row can only be shoved along as a block.
The ceiling, measured
| λ | g_max | dominoes 6 mm → 1.1 m | reach left over |
|---|---|---|---|
| 0.18 | 1.180 | 32 | 0.82 |
| 0.30 | 1.292 | 21 | 0.70 |
| 0.50 | 1.411 | 16 | 0.50 |
| 0.70 | 1.500 | 13 | 0.30 |
| 0.88 | 1.530 | 13 | 0.12 |
| 0.96 | 1.564 | 12 | 0.04 |
| 0.98 | 1.553 | 12 | 0.02 |
Half again is not available at all below λ = 0.70. The curve climbs to 1.564 and then turns over, because the gap is approaching the only hard wall in the problem: at λ = 1 the falling domino’s corner stops reaching the next face. The best growth in the game sits where there is 4% of reach left over.
One definition matters more than it looks. I first measured this over seven-domino ladders and published a ceiling several per cent too high, because a ladder a little over its ceiling does not die at the second domino. At λ = 0.75, where the ceiling is 1.502:
| of ceiling | g | #1 | #3 | #5 | #7 | #9 | #11 | #13 | dies at |
|---|---|---|---|---|---|---|---|---|---|
| 96% | 1.441 | 1.755 | 1.088 | 1.077 | 1.076 | 1.071 | 1.072 | 1.075 | runs |
| 100% | 1.502 | 1.791 | 1.085 | 1.085 | 1.081 | 1.078 | 1.076 | 1.075 | #14 |
| 102% | 1.532 | 1.809 | 1.090 | 1.086 | 1.083 | 1.081 | — | — | #9 |
| 104% | 1.562 | 1.826 | — | — | — | — | — | — | #2 |
| 112% | 1.682 | 1.895 | — | — | — | — | — | — | #2 |
I expected the 102% row to show a slow bleed, and it does not. The arrival speed holds inside one per cent for eight dominoes and then the ninth does not go over. The self-similar state is not one number — the contact height and the phase of the domino behind are in it too — so a per cent of drift in the one number you can watch is the whole margin, at the ceiling. There is no early warning available, which is the single most load-bearing fact in the piece: “it knocked the next one over” is not evidence about a step. The 104% row knocks its first domino down perfectly well and stops.
So maxGrowth runs thirteen dominoes, and the demo’s shipped CEILING table
is re-measured by scripts/measure.mjs, which fails if it has drifted.
What a chain at its ceiling is actually worth
Twenty-five builds of the same ladder, 2% of slop in every gap and 20% in the flick — roughly what a careful hand on a real table manages:
| λ | g | of ceiling | dominoes | completed | died at |
|---|---|---|---|---|---|
| 0.60 | 1.398 | 96% | 17 | 25/25 | — |
| 0.60 | 1.442 | 99% | 16 | 8/25 | #10,#11,#13,#14,#15 |
| 0.60 | 1.456 | 100% | 15 | 1/25 | #4,#5,#7,#9,#10,#12,#13,#14 |
| 0.88 | 1.469 | 96% | 15 | 25/25 | — |
| 0.88 | 1.530 | 100% | 14 | 8/25 | #11,#12,#13 |
| 0.96 | 1.501 | 96% | 14 | 25/25 | — |
| 0.96 | 1.564 | 100% | 13 | 12/25 | #11,#12 |
Four per cent of headroom costs one domino and buys all of it back. The cliff is that sharp because the ladder is self-similar: at the ceiling every step is equally marginal, so a fourteen-domino chain is fourteen coin flips, and the deaths scatter through it instead of piling up anywhere. There is no step to go and fix, and — per the table above — no reading you could have taken at #12 that would have told you. The thing that killed it at #13 is the growth factor you chose at #1.
The row that makes the point
Same growth factor, same count, same final height, different gap:
| plan | λ | g | dominoes | final | table used | completed |
|---|---|---|---|---|---|---|
| greedy | 0.96 | 1.564 | 13 | 129 cm | 273 cm | 12/25 |
| peak, backed off | 0.96 | 1.501 | 14 | 118 cm | 280 cm | 25/25 |
| half again, λ 0.75 | 0.75 | 1.500 | 14 | 117 cm | 229 cm | 14/25 |
| half again, λ 0.88 | 0.88 | 1.500 | 14 | 117 cm | 259 cm | 25/25 |
| timid | 0.50 | 1.180 | 33 | 120 cm | 453 cm | 25/25 |
Those two middle rows are the same build by every measure a builder would write down. One of them completes 14 times out of 25 and the other 25 out of 25, and the only difference is 30 cm of extra table. At λ = 0.75 the ceiling happens to be 1.502, so “half again” is sitting at 99.9% of it; at λ = 0.88 the ceiling is 1.530 and the identical chain has room. The growth factor is the number everyone argues about and the gap is the one doing the work.
That is what the demo is for. You set growth with ▲ ▼ and the gap with ◀ ▶,
and the instrument under the table plots the measured ceiling with every step
you have placed sitting against it. test ×25 is the only honest score: once
is not the same as reliably, and a chain that finished once will tell you
nothing about which it was.
The clock, which is the only thing not self-similar
| step | height | tipped at | since last | in √(h/g) |
|---|---|---|---|---|
| #4 | 20 mm | 0.295 s | 0.146 s | 3.22 |
| #8 | 103 mm | 1.303 s | 0.352 s | 3.45 |
| #11 | 346 mm | 2.801 s | 0.556 s | 2.96 |
| #13 | 778 mm | 4.505 s | 0.972 s | 3.45 |
Every step takes 3.24·√(h/g), flat to ±10% over the whole ladder — so each one takes √g ≈ 1.22× as long as the last, and half of a 4.5-second run is the last three dominoes (the last two are 38%). The geometry repeats; the clock does not. It is the one cue in the demo that tells you how far along you are, since the picture deliberately will not.
What is physics here and what isn’t
- Derived and checked against the integrator: the
h⁴scaling and the scale-free energy ratio; theτandh₁cos(a₁−a₂)moment arms; theasin(λ)lean limit, found to 0.24°; the fixed point inŵand its independence from the flick; the√(h/g)step time. - Measured, not assumed: the whole ceiling curve, the reliability cliff,
the wave speed.
node scripts/measure.mjsprints every table above and exits non-zero if any of them stops agreeing. - A modelling choice: each domino is pinned at its leading edge. Real ones slide, and on a slippery table a row skates instead of toppling — that is a real failure mode this model cannot show. Restitution is 0 (dominoes do not bounce; anything above a few per cent looks obviously wrong) and contact friction is µ = 0.4.
- Settled, not assumed: windowing, iteration order and iteration count all
change the answer by less than 0.01 in
g_max, and the timestep by less than 0.001 in arrival speed. I checked because an early version silently used its own solver window as the “has the wave advanced?” signal, which made a perfectly ordinary uniform row die at the eighth domino.
Reuse
src/half-again.mjs is framework-free with no canvas in it.
createChain(sizes, gaps)+flick(chain, ŵ)+settle(chain, {dt, onStep})is the simulator;settletakes anonStepcallback and stops on its own once the wave has stopped advancing, so it is safe to call per frame with a one-frametMaxto run a chain in real time.ladder(h0, g, lam, n)andplan({h0, target, g, lam})build the two shapes worth building;layout(sizes, gaps)gives the x positions.ceiling(lam)is the shipped curve — cheap enough to call on every keypress, whichmaxGrowth(which bisects a thirteen-domino simulation per point) is very much not.reliability(sizes, gaps, {runs, slop})is the number that matters.findContact(d1, d2)returns the penetration and both moment arms, if you want the geometry without the solver.
Port notes: the whole integrator is scalar — one angle and one angular velocity
per body, and two scalar impulses per contact — normal, then friction
clamped against it — with no vectors and no matrices anywhere. It translates
into a tick method directly. The one thing not to change casually is DT
(1/3000 s): the contact is stiff and the entire result is an impulse transfer.
The demo bundles its own copy of the module (ADR-0002) — if you touch src/,
re-copy it into demo/. node scripts/screenshot-demo.mjs regenerates the
thumb and media and doubles as the smoke test: it drives the real keyboard,
then builds the same chain at the ceiling and 4% under it and asserts the first
is a coin flip and the second is not.




