Profile
Back to NewsBack
GitHub Trending 8 min
Reader Mode
tailuge/billiards: billiards physics in the browser

tailuge/billiards: billiards physics in the browser

8 hours ago

Billiards - Free online pool and billiards game

codecov</a> CodeFactor</a> Code Smells</a> Tests</a> Open in Gitpod</a> !GitHub

Free online 3D pool and billiards game running in a browser, WebGL table viewed from behind the cue ball</a>

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 use WebGL

Features

  • Backspin, sidespin and 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, three cushion and Sagu billiards rules.
  • Deploys to GitHub Pages, Vercel and Render with GitHub Actions.
  • Runs on and was developed mostly on a potato e.g. Raspberry Pi 4.

Install

You dont, you just play in your browser at billiards.tailuge.workers.dev/lobby

If you prefer, you can install a thin client wrapper:

  • Windows / macOS / LinuxDownload
  • Android
— Driect Download or via APKPure or Obtainium

Reference material

with important corrections by Kiefl. simulation and constants 1 2 3 4 5 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 Mathavan. 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{ẏ_C}{ẋ_C}\right) \qquad s' = \sqrt{(ẋ_C)^2 + (ẏ_C)^2} $$

Numerical solutions for the centroid velocity of the ball during compression and restitution 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 e_e^2 W_{\text{compression}}$$

Some of the Mathavan equations not supplied by the paper were inferred to bridge gaps for a complete numerical solution.

Stronge compliant cushion model

Based on the book Impact Mechanics by Stronge. This analytical model accounts for the compliant nature of cushion deformation and resolves the collision across three slip regimes.

Contact velocity at the cushion contact point:

$$\vec{V}_c = \vec{v} - (\vec{\omega} \times R \hat{n})$$

Regime classification is determined by the ratio $v_{\text{ratio}} = v_{t0} / v_{n0}$ and thresholds involving the friction coefficient $\mu$, restitution $e_n$, and mass-matrix coefficients $\beta$:

| Regime | Condition | | :--- | :--- | | Gross slip | $v_{\text{ratio}} > \mu \left( (1 + e_n) \beta_{\text{ratio}} - \frac{\eta^2}{e_n} \right)$ | | Initial stick | $v_{\text{ratio}} < \mu \eta^2$ | | Slip-stick-slip | neither of the above |

where $\eta^2$ is derived from the frequency ratio $\omega_t/\omega_n$.

Velocity reconstruction from scalar solver results $v_{nf}$ and $v_{tf}$:

$$\Delta v_n = \frac{v_{nf} - v_{n0}}{\beta_n} , \quad \Delta v_t = \frac{v_{tf} - v_{t0}}{\beta_t}$$

Final updates:

$$\vec{v} \leftarrow \vec{v} + \Delta v_n \hat{n} + \Delta v_t \hat{t}$$ $$\vec{\omega} \leftarrow \vec{\omega} + \frac{mR}{I} (-\hat{n} \times \Delta v_t \hat{t})$$

Useful commands

Install

nvm use v24.11.0
corepack enable
yarn set version 4.9.1
yarn install
yarn build
yarn gltfpack

This generates artefacts in /dist for prod deployment (e.g. on github static pages)

Run

yarn serve

Then open in your browser to play

Test

yarn test
yarn coverage

Maintain

yarn deps
yarn upgrade -L
yarn prettify

Two player

yarn serve

then open to see options, message server is public nchan.

Controls

Use mouse, trackpad, touch screen or keyboard:

Aim

Control Fine aim

Topspin and backspin

Shift Side spin

Space Hit - hold for more power

A Toggle aim helper

M Masse angle

O Camera view

F Full screen

C Chat

H Help

+ - Camera height

Mouse

scroll Shot power

dbl click Hit

Progress snapshots

July 2018

!WebGL pool table rendered in the browser, July 2018

July 2019

!Ball shading and spin effects added to the WebGL table, July 2019

March 2021

!Snooker style table and refined cue rendering, March 2021

August 2023 (mobile)

top | aim :--:|:--: !Top-down camera view for planning shots on a mobile screen | !Aim view along the cue on a mobile touchscreen

Billiards gameplay video from 2026</a>

Star History

Star History Chart</a>

Licence

This project is open source and licensed under the GNU General Public License - see the LICENSE file for details. Contributions welcome.

Chat with me