Research & Reference

How to simulate a pool table in SI units

The physics an engine needs before it exists: slip velocity, the slide-to-roll transition, and why 0.4 of a radius is the only cue-tip height that matters.

A pool ball is a sphere that rolls on a plane, which sounds solved until you notice that for the first fraction of a second after you hit it, it isn't rolling at all. It's skidding — the surface touching the cloth moving faster than the ball's center. Everything that makes pool feel like pool lives in that gap between what the ball is doing and what its contact point is doing. Get the contact point right and draw, follow, english, throw, and the long dying roll all fall out of the same handful of equations. Get it wrong and you've built air hockey with dimples.

This reference was written by the Physics R&D department before a single line of engine code existed. Contract first: the equations are the spec, the engine merely implements them — in real SI units, because faking the numbers is more work than using the real ones.

The contact point is the whole game

Track two things per ball: linear velocity v = (vx, vy) in the table plane, and angular velocity ω = (ωx, ωy, ωz) in full 3D. The bottom of the ball sits at r_c = (0, 0, −R). The velocity of the material point down there, relative to the cloth, is the slip velocity:

ux = vx − R·ωy
uy = vy + R·ωx

This is the load-bearing formula. Non-zero u means the ball is sliding and kinetic friction fights the slip; zero u means it's rolling, which behaves entirely differently. Note what's not here: ωz, vertical spin. Pure english spins the contact point in place without translating it, so it never enters the slip — which is why english is a separate, slower story than topspin.

Why sliding and rolling are two different worlds

While the ball skids, friction acts opposite the slip, decelerating the center at μ_s·g and torquing the spin toward the rolling condition. Both effects push u toward zero at a constant rate — so instead of threshold-hunting frame by frame you solve for the exact moment slipping ends:

t_roll = |u₀| / ( (7/2) · μ_s · g )

That 7/2 is not a fudge factor. The contact point is braked by the linear deceleration and the angular one, and for a solid sphere the two combine as (1 + mR²/I) = 1 + 5/2 = 7/2. At t_roll the ball snaps into rolling, where a much gentler rolling resistance (≈ −μ_r·g, about 0.1 m/s²) produces the long, slow glide to a stop. Two branches, one transition, no jitter — provided you split the timestep exactly at t_roll rather than stepping past it.

Follow, draw, and the magic height of 0.4R

A shot is seeded from where the tip strikes the ball. Two offsets: b vertical (up is follow, down is draw) and a horizontal (english), each a fraction of the radius. They convert to spin directly:

ω_side = 2.5 · V0 · b / R   // topspin/backspin
ωz    = 2.5 · V0 · a / R   // english

Now the payoff. Rolling without slipping requires ωy = vx/R. Set the two equal and solve: the ball launches in pure roll exactly when b = 2/5 = 0.4 — strike four-tenths of a radius above center and there's no skid phase at all. Hit above that and the surface over-spins forward, digs in, and the ball drives on after impact: follow. Hit below center and you seed backspin; the slip points forward hard, friction hauls vx down, and if the backspin is strong enough vx crosses zero before the ball ever starts rolling — the cue ball reverses and comes back. Draw isn't a special case in the code; it's what the sliding integrator does on its own when b is negative.

One guardrail: the tip can only grip so far off-center before it slides off. That's the miscue limit, a² + b² ≤ 0.25 — stay inside the half-radius circle or the shot is a mishit.

Throw, and why english bends the shot

Ball-ball contact is a near-elastic impulse along the line of centers (e_ball ≈ 0.95), sending the object ball off close to the geometric cut angle. But the two surfaces are also moving relative to each other at the contact, and inter-ball friction acts on that relative velocity — which folds in each ball's spin, about R·ωz for a cue ball carrying english. Friction turns a slice of it into sideways motion on the object ball, a few degrees off the pure line. That's throw: how english reaches across a collision to steer a ball it's no longer touching. Small, 2 to 6 degrees, larger at slow speed, strategically real.

Cushions are the same idea against a wall. The normal component reflects and damps (e_cushion ≈ 0.82); english grabs the rail and shifts the tangential velocity, so running english widens the rebound and reverse shortens it. And because a real cushion nose contacts the ball above its equator, the impact couples into topspin — the 2/7 family of constants, same lineage as the sliding 7/2. In 2D you approximate that by blending each ball's spin partway toward the rolling state of its post-bounce velocity.

Real numbers, small steps

A regulation ball is 57.15 mm across and 170 g, with I = 2/5·mR². The friction coefficients — sliding 0.2, rolling 0.01, spinning 0.04 — are all tunables: the equations are fixed, but the feel lives in the constants. A ball counts as stopped only when both its speed drops under 1 mm/s and its spin under 0.05 rad/s; snap it to rest there or it creeps forever.

The one non-negotiable is the timestep. A ball is 2R across; let it move more than half a radius between collision checks and it tunnels straight through another ball. A 12 m/s break caps the step near 1.2 ms, so the sim runs a fixed 1 ms sub-step — about 17 per rendered frame — splitting any sub-step that contains a slide-to-roll transition. Everything else, every trick shot and safety and combination, is these equations run forward one millisecond at a time.