A physics engine looks intimidating from the outside and is surprisingly layered on the inside. Each layer depends only on the ones below it, and each one is individually simple. The complexity lives in the interactions, not in any single piece.

This guide walks up that stack, from a two-dimensional vector to a running world, using the engine behind these games as the worked example. It is aimed at anyone who wants to build one, and at players who want to know what they are playing inside.

The layers, bottom to top

Build them in this order. Each one is testable on its own, and getting a lower layer wrong makes every layer above it mysterious.

  1. Vector maths. Add, subtract, scale, dot, cross, normalise, rotate. Everything else is expressed in these.
  2. Bodies. Position, velocity, mass, shape, material properties.
  3. Integration. Turning accumulated force into new velocity and position.
  4. Broad phase. Cheaply finding pairs that might touch.
  5. Narrow phase. Exactly determining contacts.
  6. Resolution. Applying impulses so contacts look like collisions.
  7. Constraints. Ropes, joints and pins solved positionally.
  8. The world. Owning everything, running the loop, dispatching events.

Why vectors come first

Almost every bug in a homemade physics engine is a vector bug: a normal pointing the wrong way, a value normalised twice, a mutation where a copy was intended. A vector class with clear mutating and non-mutating variants prevents most of them by construction.

The vector layer

Two design decisions matter here, and both have real consequences.

Mutate or return new?

Non-mutating methods (a.plus(b) returning a new vector) are safer and allocate constantly. Mutating methods (a.add(b)) allocate nothing and cause aliasing bugs when you forget to clone.

The usual answer is both: instance methods mutate for the hot path, and statics return new vectors for clarity. The engine here does that, and provides clone() prominently so copying is easy to reach for.

What to include

Beyond the obvious arithmetic, four operations earn their place:

  • perp() — the perpendicular. Needed constantly for surface normals and for SAT axes.
  • cross() — in 2D this returns a scalar. It is how torque and angular impulses are computed.
  • reflect(normal) — bouncing, expressed once rather than reimplemented everywhere.
  • isFinite() — a guard. One NaN propagates through an entire simulation, and catching it at the vector level saves hours.

Designing the body

A body is mostly a data structure, and the decisions are about what to precompute and what to flag.

Store the inverse mass

Every resolution calculation divides by mass. Storing 1/m instead means multiplying, and it makes static bodies fall out naturally: a static body has an inverse mass of zero, so every impulse applied to it evaluates to no change at all. No special case is needed anywhere.

The same trick for rotation

Store inverse inertia. A body with rotation locked gets zero, and all the angular terms vanish automatically.

Flags worth having

FlagMeaningWhy it matters
isStaticInfinite mass, never movesWalls, floors, level geometry
isSensorDetects overlap, no responseTriggers, goal zones
fixedRotationCannot spinPlayer characters, usually
activeSkipped entirelyOff-screen or disabled objects
layer / maskCollision filteringBullets that ignore each other

Cache world vertices

A rotated polygon's world-space vertices are needed several times per step — by the broad phase, by SAT, by rendering. Recomputing them each time is wasteful; caching them with a dirty flag set on any move is a large, easy win.

Try it: Stack Physics

Drop crates from a moving crane and build a tower that stays standing.

Play Stack Physics

Choosing an integrator

Three options come up, and for arcade games the choice is usually straightforward.

MethodAccuracyStabilityBest for
Explicit EulerPoorGains energy — avoidNothing
Semi-implicit EulerAdequateExcellentAlmost everything
Velocity VerletGoodExcellentLong-running orbits and pendulums

Semi-implicit Euler is the default for good reason: it is two lines, it conserves energy well enough over game timescales, and it is what almost every commercial 2D engine uses.

The only difference from the broken version is the order: update velocity first, then use the new velocity to update position. Getting that backwards produces a simulation that slowly gains energy, and the symptom — a pendulum that swings progressively higher — is one of the most recognisable bugs in the field.

Damping is not friction

Multiplying velocity by 0.99 each step is convenient and is not friction. It is frame-rate dependent unless expressed per second, and it slows objects regardless of whether they are touching anything. Use it for air drag, not as a substitute for surface friction.

Collision architecture

The single most valuable architectural decision is separating detection from resolution completely.

Detection produces a list of contact manifolds and mutates nothing. Resolution consumes that list. Keeping them separate means you can iterate resolution several times over the same contacts, which is exactly what stacks need.

Choosing a broad phase

  • Brute force is genuinely fastest below about forty bodies. The constant factors of anything cleverer dominate at that scale.
  • Uniform spatial hash is simple, fast and ideal when objects are roughly similar in size — which is true of most arcade games.
  • Sweep and prune is excellent when motion is mostly along one axis.
  • Bounding volume hierarchies win for large static geometry, and are overkill for a browser arcade game.

The engine here picks automatically: brute force below forty bodies, spatial hash above. That threshold was found by measurement rather than assumed.

Padding the broad phase

A small margin on bounding boxes catches pairs that are about to touch, which reduces the chance of a fast object slipping through between steps. It costs a few extra narrow-phase tests and is almost always worth it.

What to tune when it feels wrong

Most 'the physics feels bad' problems map onto a small number of parameters. This table is the debugging shortcut.

SymptomLikely causeFix
Objects sink into each otherToo few solver iterationsIncrease iterations
Stacks jitterNo rest thresholdZero restitution below a closing-speed threshold
Objects vibrate at restPositional correction too aggressiveAdd slop, reduce correction percentage
Things pass through wallsTimestep too large for the speedSmaller step, or cap velocity
Simulation explodesSpring too stiff, or a zero massSoften the spring, check mass values
Everything feels floatyGravity too low for the scaleRecalibrate from fall time, not from 9.8
Controls feel unresponsiveForce applied where impulse was meantUse an impulse for instantaneous actions
Pendulum gains heightExplicit EulerSwap to semi-implicit

The pattern worth noticing is that most of these are not bugs in the sense of wrong code. They are parameters outside their stable range, which is why a physics engine needs tuning rather than debugging most of the time.

Connecting physics to gameplay

An engine that only simulates is useless. Games need to know when things happen, and the interface for that is worth designing deliberately.

Collision events

The world emits an event for each contact, carrying both bodies, the normal, the contact point and the closing speed. That last value is what lets a game distinguish a gentle bump from a genuine impact — used everywhere from sound volume to the roller-hit check in Physics Arcade Ultimate.

Sensors

A sensor detects overlap and generates no response. Goal zones, triggers and pickups are all sensors, and keeping them out of the resolution path entirely avoids a whole category of bug.

Tags and queries

Giving bodies a tag and providing byTag, bodiesAt and bodiesNear queries means game code almost never has to keep its own parallel list of objects, which is where desynchronisation bugs come from.

Derived quantities

Some values are wanted by games and awkward to compute correctly: the centre of mass of a set of bodies, total kinetic energy, whether the world has settled. Providing them once in the engine is better than every game reimplementing them slightly differently.

Try it: Newton's Playground

A sandbox with a job to do: spawn, flick, change gravity, fill the collector.

Play Newton's Playground

Testing a physics engine

Physics code is unusually hard to unit test, because the interesting behaviour is emergent and the correct answer is often 'it looks right'. A few tests are genuinely worth writing, and they catch most regressions.

Conservation tests

Momentum must be conserved in every collision. Set up two bodies, collide them, and assert that the total momentum before equals the total after within a small tolerance. This catches almost every impulse-sign and mass-weighting error.

Analytic comparisons

Some scenarios have exact answers. A projectile with no drag should reach a known peak height and a known range. A pendulum should have a known small-angle period. Comparing the simulation against the formula catches integration bugs that no amount of looking at the screen will.

Rest tests

Drop a stack of boxes, run for ten simulated seconds, and assert that total kinetic energy is below a threshold and no body has moved more than a pixel from its settled position. This catches jitter regressions, which are otherwise noticed only by players.

Determinism tests

Run the same scenario twice from the same seed and assert identical results. If a physics puzzle game is not deterministic, it is not a puzzle game, and this test is the only reliable way to know.

TestCatchesCost to write
Momentum conservationImpulse and mass errorsLow
Projectile vs formulaIntegration errorsLow
Pendulum periodAngular integration errorsLow
Stack settlesJitter, solver regressionsMedium
Determinism from seedNon-reproducible behaviourLow
Performance budgetSlow regressionsMedium

The one test not worth writing is an exact snapshot of positions after N steps. It breaks on every legitimate tuning change and teaches you nothing about whether the behaviour is right.

Key takeaways

  • Build the engine in layers: vectors, bodies, integration, broad phase, narrow phase, resolution, constraints, world.
  • Store inverse mass and inverse inertia — static bodies then need no special cases.
  • Keep collision detection and resolution completely separate so resolution can iterate.
  • Semi-implicit Euler is the right default; explicit Euler gains energy and should never be used.
  • Most 'bad physics' is a parameter outside its stable range rather than incorrect code.
  • Design the gameplay interface deliberately: collision events with impact speed, sensors, tags and queries.

Frequently asked questions

Where should I start when building a physics engine?

With a solid 2D vector class, then rigid bodies, then integration. Get objects falling and colliding with a floor before attempting anything else — most later bugs trace back to those layers.

Why store inverse mass instead of mass?

Because resolution divides by mass constantly, so storing 1/m turns divisions into multiplications. It also makes static bodies fall out naturally: an inverse mass of zero means every impulse evaluates to no change.

How many solver iterations do I need?

Four to six for general arcade physics, eight to ten if you need stable stacks. More gives diminishing returns and a real cost. Measure with your worst-case scene rather than guessing.

Why does my simulation explode?

Almost always a spring stiffer than the timestep can resolve, a body with zero or negative mass that should be static, objects that start already overlapping, or a divide-by-zero producing a NaN that propagates.

Should I write my own engine or use a library?

Use a library if physics is a means to an end. Write your own if the physics *is* the game, since arcade games usually need specific, tuned behaviour that a general engine makes awkward to reach.

What is the difference between damping and friction?

Damping reduces velocity continuously regardless of contact and models air resistance. Friction acts only at a contact between surfaces and depends on how hard they are pressed together.

References

Physical concepts in this guide were checked against the following educational sources. None of them are affiliated with Physics Arcade.

  1. Glenn Fiedler — Fix Your Timestep!
  2. HyperPhysics (Georgia State University) — Elastic and Inelastic Collisions
  3. MDN Web Docs — Canvas API
  4. NASA Glenn Research Center — Newton's Laws of Motion