Potting is the easy half. Anyone can see where the object ball has to go: it leaves along the line joining the two centres at the moment they touch, always, and no spin, speed or cleverness changes that. The hard half is the other ball — the one you have to hit again next time — and the whole of pool is the question of where it ends up.
Players get handed two rules for that. The cue ball leaves on the tangent line, perpendicular to the object ball. And the cue ball deflects about thirty degrees. Both are true. They are also flatly contradictory, nobody explains when to switch, and they are the same equation with one parameter moved.
Everything here is 2/7
A solid sphere has I = (2/5)mR². That is the only physical fact in the piece;
the rest is bookkeeping.
While the contact patch is slipping, friction acts on it, and the patch feels the friction twice — once through the centre of mass and once through the spin it induces:
u = v + ω × (0,0,−R) the velocity of the contact patch
du/dt = dv/dt + (dω/dt) × r_c
= (1 + mR²/I) · dv/dt
= (7/2) · dv/dt
So the patch’s velocity dies 7/2 times faster than the ball’s does, and by the
time the skid is over the centre of mass has given up exactly (2/7)·u₀.
Everything below is that sentence with different things substituted for u₀.
- Hit a ball dead centre and
u₀ = v₀, so it reaches natural roll having lost exactly 2/7 of its speed. - The tip offset that produces natural roll straight off the cue is
h = 2R/5— another 2/5, from the same moment of inertia. Not a tuned number. - The usual miscue limit is
R/2. There is a sliver of usable tip above natural roll, and inside it the cloth accelerates the ball: at0.5Rthe patch slips backwards at0.25v, friction pushes forward, and the cue ball comes off 7.1% faster than you hit it.
The collision, and the one number it leaves behind
Ball-on-ball is frictionless and equal-mass, so it takes the line-of-centres component and touches nothing else. The tangential velocity rides through untouched, and — the part that matters — so does the spin.
That is the whole reason the cue ball is predictable. Immediately after impact it has a small velocity and the full angular velocity it arrived with, so the patch is slipping hard, and the cloth spends the next few centimetres bending its path forward. Where it settles:
v_final = v_t·(5 + 2ρ)/7 + v_n·(5ε + 2ρ)/7
with ρ the roll fraction at contact (0 = skidding, 1 = rolling, negative =
draw) and ε = (1−e)/2 the sliver of normal velocity the impact hands back.
Divide through by the tangential coefficient and the entire shot collapses to
one number:
k = (5ε + 2ρ) / (5 + 2ρ) θ(φ) = 90° − φ − arctan(k·cot φ)
| shot | ρ | k (perfectly elastic) |
|---|---|---|
| stun | 0 | 0 — the tangent line |
| half-skidding | 0.5 | 1/6 |
| natural roll | 1 | 2/7 |
| maximum follow | 1.25 | 1/3 |
| maximum draw | −1.25 | −2/3 |
k = 0 is the tangent line. The two rules were never two rules.
The ceiling, which is the good part
Differentiate θ(φ). It is stationary when tan²φ = k, and substituting that
back collapses the arctan into the cut angle itself:
cut* = arctan(√k) θ_max = 90° − 2·arctan(√k)
The cut angle, the deflection, and the cut angle again tile a right angle. For
a rolling cue ball, k = 2/7:
θ_max = 90° − 2·arctan(√(2/7)) = 33.749°, at a cut of 28.126°
A rolling cue ball cannot deflect more than 33.75°, ever. Not at any speed,
not at any cut, not with any cue. And at the miscue limit k is exactly 1/3,
so √k = 1/√3, so the peak is exactly 30° at a cut of exactly 30°. Two
suspiciously round numbers that nobody chose.
node scripts/measure.mjs integrates the table and checks all of it:
| tip offset | ρ | k | peak, closed form | at cut | peak, measured |
|---|---|---|---|---|---|
| 0.10R | 0.25 | 0.0909 | 56.44° | 16.78° | 56.30° |
| 0.20R | 0.50 | 0.1667 | 45.58° | 22.21° | 45.50° |
| 0.30R | 0.75 | 0.2308 | 38.68° | 25.66° | 38.62° |
| 0.40R | 1.00 | 2/7 | 33.75° | 28.13° | 33.75° |
| 0.50R | 1.25 | 1/3 | 30.00° | 30.00° | 30.04° |
So where does “thirty degrees” come from?
From the fact that θ(φ) near its peak is nearly flat. The cut angle sweeps
30° while the answer moves 3.75°:
| cut | 15° | 20° | 25° | 28.13° | 35° | 40° | 45° | 55° | 70° |
|---|---|---|---|---|---|---|---|---|---|
| deflection | 28.16° | 31.87° | 33.50° | 33.75° | 32.80° | 31.20° | 29.05° | 23.69° | 14.06° |
Which is why a rule of thumb exists at all, and exactly where it stops working:
| cut | error of “30°” | miss at 80 cm of cue-ball travel |
|---|---|---|
| 8° | −11.81° | 16.5 cm |
| 12° | −5.35° | 7.5 cm |
| 17° | −0.06° | 0.1 cm |
| 28.13° | +3.75° | 5.2 cm |
| 43° | −0.03° | 0.0 cm |
| 60° | −9.37° | 13.1 cm |
| 70° | −15.94° | 22.2 cm |
Under a ball-width from 17° to 43°, and off the table by 70°. It is not a rule, it is a plateau with two cliffs, and the skill is knowing you have walked off one. The demo’s fan draws that directly: one shot line, the fullness of the hit sweeping 6° to 72°, every exit heading redrawn from a common origin and coloured by whether 30° was still true. Twelve of twenty-six, at natural roll.
The tangent line is exact, and it expires
Set ρ = 0 and θ = 90° − φ: the cue ball leaves perpendicular to the object
ball. Twelve measured shots, four cut angles, five-fold speed range:
| cut | 1.0 m/s | 2.5 m/s | 5.0 m/s |
|---|---|---|---|
| 15° | 88.74° | 89.83° | 89.92° |
| 30° | 89.42° | 89.92° | 89.96° |
| 45° | 89.66° | 89.96° | 89.98° |
| 60° | 89.81° | 89.97° | 89.99° |
Ninety degrees, to a decimal place, across a five-fold speed range and a 45°
spread of cut angle. No speed term, no cut term, no mass term. The residual —
worst at 15° and 1 m/s, where it reaches 1.3° — is not the rule failing: it is
ρ, which has already climbed to 0.015 in the 2 mm the ball is given before
contact, and which is the subject of the next section. Real balls also give
back about 2% of the normal component, and that error goes as cot φ — 1° on
a thick cut, 7° on a thin one, which is backwards from where anyone would
guess.
And then there is the thing nobody mentions. ρ is not something you set with the cue. It is something the cloth sets on the way there. The same dead-centre hit, from further away each time:
| distance to the object ball | ρ at impact | separation |
|---|---|---|
| 0.01 m | 0.013 | 89.50° |
| 0.10 m | 0.134 | 84.95° |
| 0.30 m | 0.478 | 74.47° |
| 0.60 m | 1.000 | 63.67° |
| 1.60 m | 1.000 | 63.67° |
A centre-ball hit at 1.5 m/s is fully rolling after 28 cm. So a “stun shot” across the table is not a stun shot — it is a roll shot you have mislabelled, and the tangent line you drew for it is off by 26°. The demo makes this its own screenshot: the identical shot struck at 2R/5 and struck dead centre, over 85 cm of cloth, deflect 32.57° and 32.57°. Identical to two decimals. The cue never got a vote.
Draw goes the other way — ρ negative, k negative, the cue ball comes back
through the tangent line — and the same formula covers it without a special
case:
| tip | ρ | 20° cut | 40° cut |
|---|---|---|---|
| −0.20R | −0.50 | 104.38° | 66.53° |
| −0.35R | −0.87 | 125.89° | 82.63° |
| −0.50R | −1.25 | 139.97° | 99.96° |
Three beliefs, one drill
The game layer is thin on purpose: pot the ball, stop the cue ball on a mark. Nothing in it knows about any of the above. So the measure script runs 640 generated layouts — each one built from a cut angle rather than found by rejection sampling, each confirmed by playing it first — and sends three autopilots at them. They pot identically and share one distance model. The only thing under test is what each believes about which way the cue ball leaves.
| pilot | potted | within 12 cm | within 5 cm | mean miss |
|---|---|---|---|---|
| “it follows the tangent line” | 70.8% | 57.8% | 3.8% | 12.1 cm |
| “thirty degrees” | 100.0% | 100.0% | 100.0% | 2.5 cm |
| the closed form | 100.0% | 100.0% | 100.0% | 2.6 cm |
One constant, carried in the head with no trigonometry, is worth as much as the exact model — inside the plateau. That is the honest version of the folklore: not that 30° is right, but that 30° is right enough over a wide enough band that the arithmetic buys you nothing, and the thing actually worth knowing is the band’s edges.
The tangent-line pilot pots only 71% because its speed model is fed by its direction model, and a shot aimed to stop somewhere unreachable comes up short. That is a real consequence of a wrong belief rather than a harness artefact, but it does mean its 12 cm is flattered — it is partly failing earlier.
What is physics here and what isn’t
- Derived: the 7/2 slip decay and the 2/7 it leaves behind,
k,θ(φ), the ceiling and the cut it happens at, the 2R/5 natural-roll tip offset, the 7.1% free speed at the miscue limit, roll-out and distance-to-roll. - A measured input: the cloth coefficients (0.2 sliding, 0.01 rolling,
0.044 spin) and the restitutions (0.96 ball, 0.75 cushion). Standard quoted
values, not fitted to make anything come out nicely — the ideal columns above
are all run at
e = 1so the closed form is being checked rather than tuned to. - Deliberately not modelled: collision-induced throw, and squirt. Both are real and both are empirical curves in cut angle, surface speed and how clean the balls are. They are not things that fall out of a line of algebra, and this piece is about the parts that do — so there is no side english on the cue, and the object ball leaves along the line of centres to within 0.000°.
- A table, not physics: the pockets are capture circles with no jaws, so nothing rattles; and the cushion keeps the ball’s spin and lets the cloth sort it out over the next few centimetres, which is roughly but not exactly what a real rail does.
The first version of stepBall chattered across the slip/roll boundary and
put a few degrees of noise into every deflection. It now lands on u = 0
exactly — if a step would carry the patch velocity through zero, it takes the
(2/7)u out of the centre of mass in one go and switches to rolling. That is
why the measured column matches the closed form to 0.00° instead of 0.4°.
Reuse
src/tangent.mjs is framework-free with no canvas in it.
makeWorld(balls, opts)/step(world, dt)— 1 ms substeps with continuous ball-ball detection, because a thin cut resolved a millimetre late is a thin cut resolved at the wrong angle.stepreturns events (hitwith both pre-impact states,pot,rail);settle(world)runs to rest.shoot(ball, {angle, speed, tipY, tipX}),rollFraction,contactVel.contactFromAim(cue, obj, angle)— the geometry read forwards, for a live readout: the player aims a line and the cut angle is a consequence of it.ghostBall/cutAngleread it backwards, from a target.deflectionK,deflectionDeg,deflectionPeak,deflectedDir,deflectedSpeedFrac— the closed forms above.rollOutDistance,speedForDistance,distanceToRoll— the distance half, which no rule of thumb helps with at all.PILOTSandplanShot(...)— the three beliefs, for scoring them.
The demo is demo/index.html with its own copy of the module (ADR-0002):
pointer or arrow keys to aim, hold to charge and release to shoot, ↑/↓ for tip
height against a drawn miscue limit, F for the fan. Every aim line it draws
is clipped at the first cushion, because the prediction has no rail in it and
drawing it through one would be claiming something the line does not know.
node scripts/screenshot-demo.mjs regenerates the thumb and media and is the
smoke test: it fires a real pointer-drag, checks the demo’s aim-time prediction
against its own simulation to 2.5°, and asserts that the same cut struck dead
centre from 8 cm separates more than 15° wider than struck rolling — it is 24°
in practice, 85.1° against 60.7°. That last one is
written as a comparison rather than ”≈ 90°” on purpose — the first version
asserted 90° from the normal shooting distance and failed, correctly, because
over 85 cm of cloth there is no such thing as a stun shot.






