Skip to content

One file of physics, five games

Verlet integration, distance constraints and circle collision in 276 lines of plain JavaScript, with no dependencies and no build step. Everything below is running that file — the same one the games load.

Drag anything · Cut then drag to tear the cloth · Gold dots are pinned · Line colour is strain

What it is

Verlet integration stores a point's current and previous position instead of its velocity. Velocity is implied by the gap between them, and that one decision is why the whole file is short.

A distance constraint is solved by moving two points toward or away from each other — correcting the position corrects the velocity for free, so there is no impulse solver to write. And moving a point without moving its previous position is applying a velocity, so there is no force API either. Those two facts give you ropes, cloth, ragdolls, soft bodies and trusses out of about eight functions.

What it deliberately does not do: rotating rigid bodies, stacking contact resolution, friction cones. Shapes are built from points and constraints instead, and anything that has to collide is round, because circles collide predictably and boxes made of four points catch corners on each other.

Read the engine Read this demo

Five ways I got it wrong

Physics code fails quietly. A wrong answer is still a number, the frame still renders, and the only symptom is a game that feels bad in a way you cannot name. I wrote up the five bugs that cost me the most — breaking a beam inside the solver instead of after it, the quadratic impulse a driven pinned point hands its neighbour, a cosmetic flame that ate the gameplay random stream, and two ways of asking an element where it is that are both wrong — with the broken code path actually executed rather than described.

Five Bugs I Wrote Into a 276-Line Physics Engine

Built on it

Five games, all free, none asking you to sign in

How it is verified

Physics you cannot check is decoration. Each of these games ships with a script that drives the real page and asserts something that could actually be false:

  • Span builds both bridges through the live game and asserts that the unbraced deck fails and the triangulated one carries the load. If that ever stops being true, the advice on the page is a lie.
  • Upright is played by a proportional-derivative controller and separately by nothing at all. The pole has to fall on its own and stay up under control — a game that fails the first is broken and one that fails the second is unwinnable.
  • Space Dodge is run against a virtual clock at several refresh rates, because a fixed timestep is a claim about frame-rate independence and claims are worth nothing unmeasured.

The same applies to the numbers in the write-up. They come from tools/measure-engine.js, which loads this exact verlet.js and runs each bug's wrong version alongside its right one.