Start with a Vec2 that can add and take a dot product and end with a world that drops a box onto the ground and lets it settle at rest. Every lesson is one concrete spec with exact values: why semi-implicit Euler beats explicit, the impulse that makes a head-on bounce reverse, the minimum-penetration axis from SAT, and the r x n terms that turn a contact into spin.
Over 44 lessons you build a working 2D rigid-body physics engine from scratch: a Vec2 math kit (add, subtract, scale, dot, length, normalize, and the 2D scalar cross), particles you move with numerical integration (explicit vs semi-implicit Euler, forces, gravity, a force accumulator, and a fixed timestep), rigid bodies with mass and inverse mass so a static body has infinite mass and never moves, shapes (circle, AABB, convex polygon) with world transforms, collision detection that returns an exact contact normal and penetration depth for every shape pair (circle-circle, AABB-AABB, circle-AABB, polygon-polygon and circle-polygon via the Separating Axis Theorem), impulse-based resolution with restitution and positional correction, rotation with moment of inertia and contact-point impulses that produce spin, Coulomb friction, and a world step that integrates, detects, and resolves every frame.
By the end you have an importable engine and a runnable, asset-free text demo: it steps a small scene under gravity and prints each body's position over time, ending with a box that drops onto the ground and settles at rest without tunneling through it. The engine is deterministic and its public API is fully tested, so every behavior is pinned to an exact value.
This is a teaching-grade engine built around the standard impulse-with-restitution design that Box2D and similar 2D engines use: sequential-impulse resolution over a per-frame contact list, with positional correction for sinkage. It is honest about what it does not do - it resolves each contact at a single point, derives a Coulomb friction impulse but leaves wiring it into the resolver as the first extension, uses a small fixed number of solver iterations rather than a full warm-started constraint solver, has no continuous collision detection (very fast bodies can tunnel), no joints or resting/sleeping, and a simple grid-free broadphase. What you finish with is the honest core that production 2D engines extend with friction, warm starting, two-point manifolds, and richer constraints.
A physics engine is arithmetic on 2D vectors, so that is where everything begins. Today you build the Vec2 type and its first operation, addition, so every later lesson has positions and velocities to work with.
Create a Vec2 value type with X and Y components and an addition that sums them componentwise.
Every quantity in a physics engine - a position, a velocity, a force, a contact normal - is a 2D vector: a pair of numbers you can do arithmetic on. Before any of the interesting motion or collision code can exist, that value type has to exist, along with the operations that combine vectors. The most basic of those is addition, which sums the two components independently: moving a point by an offset, or combining two velocities, is just adding vectors.
Use plain float64 components so results stay exact for the clean inputs the specs
use. Keep Add a pure function that returns a brand-new Vec2 rather than
mutating its inputs - the rest of the engine leans on vectors being simple,
copyable values, and that convention starts today.
// the value type every position, velocity, and force will betype Vec2 struct{ X, Y float64 }// componentwise sum, returns a new vectorfunc Add(a, b Vec2) Vec2 {return Vec2{ /* X + X, Y + Y */ }}
A genuinely working 2D rigid-body engine - semi-implicit integration, mass and inverse mass, circle/box/convex-polygon collision with exact contact manifolds via SAT, and impulse resolution with restitution, positional correction, and rotation behind a world step that settles a scene - but contacts are resolved at a single point, a Coulomb friction impulse is derived yet not applied in the resolver, and there is no warm starting, continuous collision, joints, or sleeping.
The classic rigid-body dynamics series. Part 3 derives the impulse-with-restitution collision response, including the r x n rotational terms, that the resolution chapter is built around.
The Box2D author's GDC talks on sequential impulses, the contact solver, and positional correction - the design this project follows for resolving a per-frame contact list.
A step-by-step impulse-engine tutorial: manifolds, normal and penetration, impulse resolution, positional correction, and friction - the same arc, worked in code.
The reference for the geometry: closest-point tests, AABBs, and the Separating Axis Theorem used for the polygon collision detection in chapter four.
A full book that builds a particle engine and then a rigid-body engine in the same order this project does - integration first, then contacts, then rotation.
The Hitman/Verlet paper - a different integration and constraint approach worth reading once you finish, to see the road not taken.