Physics overview
Meep ships a rigid-body physics engine in the box — pure JavaScript, built for massive worlds, fully debuggable, and bit-exact across V8 runtimes.
Meep comes with a rigid-body physics engine built in. Not a binding to a WebAssembly blob, not a third-party library you wire up yourself — an engine, written in the same JavaScript as the rest of Meep, that you turn on by registering two systems.
Built in, not bolted on
Most web engines don’t ship physics. Three.js, Babylon, and PlayCanvas all tell you to bring your own — Rapier, Cannon, Ammo, Havok — and give you an integration layer at best. That means a second runtime to load, a WASM heap that lives outside your debugger, and a simulation whose source you can’t step into.
Meep’s physics is part of the engine. A body is three components on one entity, and the simulation is two systems:
import { PhysicsSystem } from "@woosh/meep-engine/src/engine/physics/ecs/PhysicsSystem.js";
import { ColliderObserverSystem } from "@woosh/meep-engine/src/engine/physics/ecs/ColliderObserverSystem.js";
const physics = new PhysicsSystem();
em.addSystem(physics); // the solver
em.addSystem(new ColliderObserverSystem(physics)); // attaches colliders to bodies
That’s the whole setup. From here, any entity carrying Transform + RigidBody + Collider falls under gravity, collides, and comes to rest — see Rigid bodies and Colliders & shapes.
Optimized for massive worlds
The simulation cost tracks the active set, not the size of the world:
- Two spatial indexes — one for static geometry, one for moving bodies. Level geometry is added once and never rebuilt; only moving bodies pay a per-frame cost.
- Solving in groups. Bodies that are touching are solved together as a group, and separate groups are solved independently. Static and kinematic bodies anchor a group without enlarging it.
- Sleeping in bulk. A pile that comes to rest sleeps as a unit and drops out of the active set entirely. A hundred-block stack wakes in a single step when something hits it — no frame-by-frame ripple up the stack.
A scene can hold an enormous static world and thousands of resting bodies while the per-frame budget stays proportional to what’s actually moving.
Fully debuggable — it’s all JavaScript
Because the solver is plain JS, it lives in the same world as your gameplay code. Set a breakpoint inside the collision code. Step through a contact being resolved. Read a body’s velocity in the console mid-frame. There is no opaque WASM heap and no separate runtime — the same browser DevTools you use for the rest of your page reach all the way down.
In development, thousands of runtime assertions guard the physics invariants and fail loudly at the source of the problem. In production, the strip plugin removes them for native-speed runtime.
Deterministic and bit-exact across V8
The simulation advances on a fixed timestep and uses no wall-clock, no hardware randomness, and no platform math beyond IEEE-754 doubles. Given the same inputs in the same order, it produces the same result, bit for bit, on every V8 runtime — Chrome, Edge, Node, Deno, Electron.
That’s the foundation for:
- Lockstep multiplayer — replicate inputs, not state. Every client simulates the same world.
- Replays — record the input stream, replay the exact match.
- Server reconciliation — the server re-runs the client’s steps and gets the identical answer.
- Reproducible tests — a physics regression is a diff, not a flake.
See Determinism for the guarantees and the rules that preserve them.
Where to go next
- Rigid bodies —
RigidBody, body kinds, mass, forces, sleeping. - Colliders & shapes — shapes, materials, layers, sensors, contact events.
- Spatial queries — raycasts, shape casts, and overlap tests against the live world.
- Joints — connect bodies with ball-sockets, hinges, sliders, and welds.
- Constraints — per-DOF limits, springs, and motors on a joint.
- Determinism — the fixed-step contract and what bit-exact buys you.
- Raining rigid bodies and Jointed chains — live demos.
- The source:
src/engine/physics/ecs/— start withPhysicsSystem.js.