Web physics engine. A from-scratch TypeScript port of Box3D by Erin Catto, bit-exact against the C reference. Multithreaded by default. Zero dependencies, tree-shakeable, ESM only.
0.x: the API tracks Box3D until its 1.0. Pinned to box3d
29bf523(v0.1.0+ fixes).
npm install tumble.js
import { init, World, BodyType, makeBoxHull } from "tumble.js";
await init(); // compiles the inlined wasm kernel + resolves threading
const world = new World();
const ground = world.createBody({ position: { x: 0, y: -1, z: 0 } });
ground.createHull({}, makeBoxHull(50, 1, 50));
const box = world.createBody({ type: BodyType.Dynamic, position: { x: 0, y: 10, z: 0 } });
box.createHull({ density: 1 }, makeBoxHull(0.5, 0.5, 0.5));
for (let i = 0; i < 120; i++) world.step(1 / 60, 4);
console.log(box.getPosition()); // rests at y ≈ 1.5Shapes (sphere, capsule, convex hull, mesh, heightfield, compound), eight joint types, continuous collision, ray/shape/overlap queries, contact + sensor events, kinematic character mover. Live samples on real WebGPU: dylanebert.github.io/tumble.js.
init() resolves threading per host. Results are bit-identical at any thread count.
| host | kernel |
|---|---|
| bun / node | multithreaded, 4 threads |
| browser, cross-origin isolated | multithreaded, 4 threads |
| browser without COOP/COEP | single-threaded, one console note |
init({ threads: 0 | n }) |
force single-thread / set count |
Browser multithreading needs two headers: Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. Hosts without them run single-threaded. threads() reports what resolved; no shutdown() is needed.
Regenerated each release by bun run bench on one runner. The vs box3d badge is the chart's default group: tumble on bun against the C original, same scene, same machine, both at 4 threads.
That baseline is native clang with -march=native. The chart also plots box3d compiled to wasm via emscripten, the fastest box3d a browser can actually run: tumble trails that deployable ceiling but buys a readable, tree-shakeable, TS-native surface in return.
| gate | scope | status |
|---|---|---|
bun test |
box3d's unit suite, ported | ci badge |
| fixture gate | every scene fixture, per-step world-state hash equality vs C | maintainer-run |
| MT fixture gate | same fixtures through the pool, bit-exact at any thread count | maintainer-run |
| upstream parity | in-scope upstream tests covered | parity badge |
Equality is exact, not tolerance-banded: every f32 op mirrors the C expression tree (per-op Math.fround, no FMA, portable trig, scalar min/max). Every upstream test carries a disposition in upstream/parity.json, cross-checked in CI.
MIT. Port © Dylan Ebert; Box3D © Erin Catto. See LICENSE.
Issues and bug reports are welcome. The port isn't open to code contributions.