Billiards
billiards physics in the browser
Install / Use
/learn @tailuge/BilliardsREADME
billiards
This is an open-source project bringing unsophisticated billiards physics written in typescript to the browser.
Online Demo
Demos run in all major desktop and mobile browsers and uses WebGL
- Nine ball ⬀ make a break and share replay link with friends
- Three cushion billiards ⬀ the ultimate test of physics and player (average on both counts)
- Snooker ⬀ we await the first 147 submission to the leaderboard.
- Play vs the Claw ⬀.
- Try two player online lobby using nchan
- Try to get on the leaderboard of highest breaks hosted on vercel.com
- Inspect physics and tweak constants using diagrams.
Features
- Backspin, sidespin an cushion bounces well modeled.
- Presentation using WebGL in any modern browser on mobile, linux, mac or windows.
- Record and playback breaks.
- Two player online mode with nchan nginx server.
- Nine ball, snooker and three cushion billiards rules.
- Deploys to github pages, vercel.com and render.com with github actions.
- Runs on and was developed mostly on a potato e.g. Raspberry pi 4.
Reference material
- Papers on ball mechanics Han 2005 with important corrections by Kiefl.
- cushions, max spin, simulation and constants 1 2 3 4 5
- 3D graphics uses three.js
- Inline LaTeX editor for equations in README.md
Key equations
Based on Han 2005 paper
surface velocity
$$\vec{v}_a = \vec{v} + (\vec{up} \times R\vec{\omega})$$
sliding motion
$$\dot{v} = -\mu g \frac{\vec{v}_a}{|\vec{v}_a|}$$
$$\dot{\omega} = -\frac{5}{2}\frac{\mu g}{R} \frac{\vec{v}_a}{|\vec{v}_a|}$$
$$\dot{\omega}_z = -\frac{5}{2}\frac{M_z}{mR^2} \text{sgn}(\omega_z)$$
rolling motion
$$\dot{v} = -\frac{5}{7}\frac{M_{xy}}{mR} \frac{\vec{up} \times \vec{\omega}}{|\vec{\omega}|}$$
$$\dot{\omega} = -\frac{5}{7}\frac{M_{xy}}{mR^2} \frac{\vec{\omega}}{|\vec{\omega}|}$$
where
$M_{xy} = \frac{7}{5\sqrt{2}} R \mu m g$ , $M_z = \frac{2}{3} \mu m g \rho$
collisions
Based on paper by Alciatore incorporating throw effect due to the small amount of friction between balls. Figures to prove consistency between the code and paper here.
For ball $a$:
$$\vec{v}a \leftarrow \vec{v}a + \frac{J{\text{normal}}}{m}\hat{n} + \frac{J{\text{tangential}}}{m}\hat{t}$$
$$\vec{\omega}_a \leftarrow \vec{\omega}_a + \frac{1}{I} (\vec{r}a \times \vec{J}{\text{tangential}})$$
For ball $b$:
$$\vec{v}b \leftarrow \vec{v}b - \frac{J{\text{normal}}}{m}\hat{n} - \frac{J{\text{tangential}}}{m}\hat{t}$$
$$\vec{\omega}_b \leftarrow \vec{\omega}_b + \frac{1}{I} (\vec{r}b \times \vec{J}{\text{tangential}})$$
Where:
The relative velocity at the point of contact is computed as:
$$\vec{v}_{\text{rel}} = (\vec{v}_a - \vec{v}_b) + \vec{r}_a \times \vec{\omega}_a - \vec{r}_b \times \vec{\omega}_b$$
$\vec{v}{\text{slip}} = \vec{v}{\text{rel}} - (\vec{v}_{\text{rel}} \cdot \hat{n}) \hat{n}$
$\vec{r}_a = -R \cdot \hat{n}$ and $\vec{r}_b = R \cdot \hat{n}$
$J_{\text{normal}} = \frac{-(1 + e)v_{\text{rel,normal}}}{(2/m)}$
$J_{\text{tangential}} = \min\left( \frac{\mu J_{\text{normal}}}{v_{\text{rel}}}, \frac{1}{7} \right)(-v_{\text{rel,tangential}})$
$\hat{n}$: normal unit vector along the line of centers.
$\hat{t}$: tangential unit vector perpendicular to $\hat{n}$.
cushion bounce
This is based on a paper by Mathaven. Many of the figures from the paper are recreated to confirm correctness.
Slip velocity at cushion contact point I
$$ ẋ_I = \dot{v_x} + \dot{\omega_y} R \sin \theta - \dot{\omega_z} R \cos \theta \qquad ẏ'_I = -\dot{v_y} \sin \theta + \dot{\omega_x} R $$
$$ \phi = \arctan\left(\frac{ẏ'_I}{ẋ_I}\right) \qquad s = \sqrt{(ẋ_I)^2 + (ẏ'_I)^2} $$
Slip velocity at table contact point C
$$ ẋ_C = \dot{v_x} - \dot{\omega_y} R \qquad ẏ_C = \dot{v_y} + \dot{\omega_x} R $$
$$ \phi' = \arctan\left(\frac{ẏ'_I}{ẋ_I}\right) \qquad s' = \sqrt{(ẋ_C)^2 + (ẏ_C)^2} $$
Numerical solutions for the centroid velocity of the ball during compression and resititution phases.
$$ (\dot{v_x})_{n+1} - (\dot{v_x})_n = - \frac{1}{M} \left[\mu_w \cos(\phi) + \mu_s \cos(\phi') \cdot (\sin \theta + \mu_w \sin(\phi) \cos \theta)\right] \Delta P_I $$
$$ (\dot{v_y})_{n+1} - (\dot{v_y})_n = - \frac{1}{M} \left[ \cos \theta - \mu_w \sin \theta \sin \phi + \mu_s \sin \phi' \cdot \left( \sin \theta + \mu_w \sin \phi \cos \theta \right) \right] \Delta P_I $$
Numerical solutions for angular velocity of ball
$$ (\dot{\omega_x})_{n+1}−(\dot{\omega_x})_n = -\frac{5}{2MR}[\mu_w \sin(\phi) + \mu_s \sin(\phi') \times (\sin(\theta) + \mu_w \sin(\phi)\cos(\theta))]\Delta P_I $$
$$ (\dot{\omega_y})_{n+1}−(\dot{\omega_y})_n = -\frac{5}{2MR}[\mu_w \cos(\phi)\sin(\theta) - \mu_s \cos(\phi') \times (\sin(\theta) + \mu_w \sin(\phi)\cos(\theta))]\Delta P_I $$
$$ (\dot{\omega_z})_{n+1}−(\dot{\omega_z})_n = \frac{5}{2MR}(\mu_w \cos(\phi)\cos(\theta))\Delta P_I $$
$\theta$ is a constant of the angle of cushion contact above ball centre with $\sin(\theta) = 2/5$. $\mu_s$ is the coefficient of sliding friction between the ball and table surface. $\mu_w$ is the coefficient of sliding friction between the ball and the cushion.
Work done by the normal force at contact point $I$ along the $Z'$-axis which is aligned from the ball centre to I
$$ W_{Z'}^I(P_I^{(n+1)}) = W_{Z'}^I(P_I^{(n)}) + \frac{\Delta P_I}{2} \left( z'_I(P_I^{(n+1)}) + z'_I(P_I^{(n)}) \right) $$
The ball is assumed to be bouncing in the +y cushion. Compression phase iterates until
$$\dot{v}_y \le 0$$
For the restitution phase the iteration continues until the work done is
$$W_{Z'}^I \ge (1 - e_e^2) W_{\text{compression}}$$
Some of the Mathaven equations not supplied by the paper were inferred by LLMs and the code for them was initially generated by a combination of Claude, Qwen and GPT-4o.
Useful commands
Install
nvm use v24.11.0
corepack enable
yarn set version 4.9.1
yarn install
yarn dev
yarn gltfpack
This generates artefacts in /dist for prod deployment (e.g. on github static pages)
Run
yarn serve
Then open http://localhost:8080/ in your browser to play
Test
yarn test
yarn coverage
Maintain
yarn deps
yarn upgrade -L
yarn prettify
Two player
yarn serve
then open http://localhost:8080/multi.html to see options, message server is public nchan.
Controls
Use mouse, touch screen or keyboard:
<kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇦</kbd> <kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇨</kbd> Aim
<kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">Control</kbd> <kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇦</kbd> <kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇨</kbd> Fine aim
<kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇧</kbd> <kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">⇩</kbd> Topspin and backspin
<kbd style="border: 1px solid #aaa; border-radius: 0.2em; padding: 0.1em 0.3em; font-size: 0.85em;">Shift</kbd> <kbd style="border: 1px solid #aaa; border-radius: 0.2e

