Reference · Physics · Three.js · WebAssembly
📅 March 2026 ⏱ ≈ 10 min read 🔄 Updated: March 2026

JavaScript Physics Engines Comparison 2026 — Cannon-es, Rapier, Ammo.js, OimoPhysics

Choosing a physics engine for a Three.js project involves trade-offs between bundle size, simulation quality, WASM startup latency, API ergonomics, and constraint support. This reference covers the four most-used options plus emerging alternatives, with code snippets showing the minimal integration for each.

1. Feature Comparison Table

Feature Cannon-es Rapier.jsWASM Ammo.jsWASM OimoPhysics
Language TypeScript Rust → WASM C++ Bullet → WASM JavaScript
Bundle size (gzip) ~55 KB ~180 KB ~750 KB ~45 KB
Rigid bodies
Convex shapes △ limited
Concave (trimesh) △ static only
Joints/Constraints ✓ basic ✓ full ✓ full (Bullet) △ basic
Soft bodies / cloth ✓ (Bullet)
CCD (tunneling prevent) △ threshold
Deterministic replay ✓ (Rust)
TypeScript types ✓ native ✓ generated △ community
Three.js helper lib cannon-es @dimforge/rapier3d three/examples/jsm
Performance (10k bodies) ~45 fps ~58 fps ~50 fps ~52 fps
Recommendation Beginner Production Soft bodies Legacy

Performance figures are approximate at 1000 dynamic boxes, desktop Chrome, M2 MacBook. △ = partial support.

2. Cannon-es

cannon-es is a community-maintained fork of the original cannon.js, rewritten in TypeScript. It is the most beginner-friendly option and used throughout this site's simulations.

// CDN import (ES Module)
import * as CANNON from 'https://cdn.jsdelivr.net/npm/cannon-es@0.20.0/dist/cannon-es.js';

const world = new CANNON.World({ gravity: new CANNON.Vec3(0, -9.82, 0) });
const body  = new CANNON.Body({
  mass: 1,
  shape: new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5)),
  position: new CANNON.Vec3(0, 5, 0),
});
world.addBody(body);

// Sync Three.js mesh per frame
mesh.position.copy(body.position);
mesh.quaternion.copy(body.quaternion);

3. Rapier.js (WASM)

Rapier is written in Rust, compiled to WebAssembly, and designed from the ground up for correctness, performance, and determinism. It is the best choice for production games and simulations that require accurate CCD and complex constraints.

import RAPIER from 'https://cdn.skypack.dev/@dimforge/rapier3d-compat';

await RAPIER.init();
const gravity = { x: 0, y: -9.81, z: 0 };
const world = new RAPIER.World(gravity);

const bodyDesc = RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 5, 0);
const rigidBody = world.createRigidBody(bodyDesc);

const colliderDesc = RAPIER.ColliderDesc.cuboid(.5, .5, .5);
world.createCollider(colliderDesc, rigidBody);

// Step the world
world.step();
const pos = rigidBody.translation(); // { x, y, z }
Rapier + Three.js helper: The community @pmndrs/cannon and @react-three/rapier packages provide declarative React Three Fiber bindings. For vanilla Three.js, sync is manual (same as Cannon-es pattern above).

4. Ammo.js (Bullet Physics)

Ammo.js is an Emscripten compilation of the Bullet Physics C++ library — the same engine used in Blender, Maya, and many AAA games. It provides the widest feature set, including soft bodies, cloth simulation, and vehicle dynamics.

API ergonomics: Ammo.js mirrors the C++ Bullet API verbatim — including manual memory management (Ammo.destroy() required for heap objects). This makes it significantly more verbose than Cannon-es or Rapier. Use it when you specifically need soft bodies or vehicle physics.

5. OimoPhysics

OimoPhysics is a pure-JavaScript port of the Java Oimo Physics engine. Smallest bundle, decent performance for simple scenes, but limited features and no active development since 2016.

6. How to Choose

Emerging option — Jolt Physics: Jolt Physics (C++, MIT) by Jorrit Rouwe (used in Horizon Zero Dawn) compiles to WASM and is beginning to appear in JS wrappers (jolt-physics npm package). Early benchmarks show 2× Rapier speed for large scenes. Watch for 2026 maturity.