Planck.js
~290kb min / ~68kb gzipBox2D rewritten in JavaScript — real 2D physics for games and simulations.
See It in Action
Real rigid-body physics — collisions, joints, and gravity that CSS cannot fake.
Boxes and circles tumble onto a platform — rigid body dynamics with real collision, rotation, and stacking that CSS cannot simulate.
What is Planck.js?
Planck.js is a JavaScript rewrite of Box2D, the 2D physics engine behind Angry Birds, Limbo, and thousands of other games. You create a "world" with gravity, drop rigid bodies into it (boxes, circles, polygons), and the engine figures out how they collide, bounce, and stack. You handle the rendering — Canvas, SVG, PixiJS, whatever you prefer.
It ships with the full Box2D joint system (revolute, prismatic, distance, weld, pulley, gear, wheel, rope) so you can build ragdolls, vehicles, bridges, and mechanical contraptions. Unlike Matter.js it has no built-in renderer — you wire up your own draw loop.
When to Use Planck.js
How Planck.js Compares
Planck.js uses Box2D's proven algorithms and has more joint types. Matter.js includes a built-in renderer and a simpler API, making it easier for quick prototypes.
Different dimensions. Planck.js is 2D, Cannon.js is 3D. Pick based on your project.
Rapier is a Rust/WASM engine with both 2D and 3D modes and faster performance. Planck.js is pure JavaScript — no WASM init step, familiar Box2D API.
Get Started with Planck.js
npm install planck
yarn add planck
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/planck.min.js"></script>
import { World, Edge, Box, Circle } from "planck";
// Create world with gravity
const world = new World({ gravity: { x: 0, y: -10 } });
// Static ground (edge shape)
const ground = world.createBody({ type: "static" });
ground.createFixture({ shape: new Edge({ x: -20, y: 0 }, { x: 20, y: 0 }) });
// Dynamic box
const box = world.createBody({ type: "dynamic", position: { x: 0, y: 10 } });
box.createFixture({ shape: new Box(0.5, 0.5), density: 1, friction: 0.3 });
// Dynamic circle
const ball = world.createBody({ type: "dynamic", position: { x: 2, y: 15 } });
ball.createFixture({ shape: new Circle(0.4), density: 1, restitution: 0.7 });
// Run simulation at 60 Hz
setInterval(() => {
world.step(1 / 60);
const pos = box.getPosition();
console.log("Box y:", pos.y.toFixed(2));
}, 1000 / 60);