npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

box3d-wasm

v0.2.0

Published

Box3D, Erin Catto's 3D physics engine, compiled to WebAssembly with SIMD and optional wasm threads. Isomorphic: works in browsers and Node.js, auto-detecting thread support.

Downloads

439

Readme

box3d-wasm

Box3D compiled to WebAssembly, with SIMD and optional wasm threads. Works in browsers and Node.js from a single package.

Box3D is a 3D rigid body physics engine written by Erin Catto, the author of Box2D. All engine design and implementation credit belongs to him. This package only compiles his library to wasm and adds a JavaScript binding layer. Box3D is MIT licensed by Erin Catto; the full upstream license ships in this package as LICENSE.box3d.txt.

Install

npm i box3d-wasm

Quick start

import Box3D from 'box3d-wasm';

const b3 = await Box3D();

const world = new b3.World({ gravity: { x: 0, y: -10, z: 0 } });

const ground = world.createBody({ type: 'static', position: { x: 0, y: -0.5, z: 0 } });
ground.createBox({ halfExtents: { x: 20, y: 0.5, z: 20 } });

const body = world.createBody({ type: 'dynamic', position: { x: 0, y: 5, z: 0 } });
body.createBox({ halfExtents: { x: 0.5, y: 0.5, z: 0.5 }, density: 1, friction: 0.5 });

for (let i = 0; i < 120; i++) {
  world.step(1 / 60, 4);
}

console.log(body.getPosition()); // { x: ~0, y: ~0.5, z: ~0 }

The same code runs in Node.js and in the browser. The default import auto-detects thread support and loads the best build. Both builds use wasm SIMD, which every modern browser and Node.js supports. The wasm file is loaded relative to the module, so bundlers that understand new URL(..., import.meta.url) (Vite, webpack 5, Rollup) pick it up automatically.

Flavours

| import | threads | picked by the default import when | | --- | --- | --- | | box3d-wasm/deluxe | yes | SharedArrayBuffer is usable (Node.js, or a cross-origin isolated page) | | box3d-wasm/standard | no | everything else |

box3d-wasm (the default import) runs the detection above and returns whichever module fits. Import a specific flavour directly when you want to skip detection:

import Box3D from 'box3d-wasm/deluxe';

const b3 = await Box3D();
const world = new b3.World({ gravity: { x: 0, y: -10, z: 0 }, workerCount: 4 });

workerCount enables Box3D's internal multithreaded solver. It is clamped to [1, b3.maxWorkers]. The single threaded builds ignore it. Check b3.threaded at runtime to see which build you got.

Serving requirements for threads

Wasm threads use SharedArrayBuffer. Browsers require the page to be cross-origin isolated, so serve your app with:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Node.js needs no special setup; worker threads are used automatically.

API overview

Vectors are plain objects { x, y, z } and quaternions are { x, y, z, w }, so values pass directly to and from libraries like three.js.

World

const world = new b3.World({
  gravity: { x: 0, y: -10, z: 0 },
  enableSleep: true,
  enableContinuous: true,
  workerCount: 4, // deluxe build only
});

world.step(1 / 60, 4);          // timeStep, subStepCount
world.setGravity({ x: 0, y: -3.7, z: 0 });
world.getAwakeBodyCount();
world.castRayClosest(origin, translation, filter);
world.explode({ position, radius: 3, falloff: 2, impulsePerArea: 10 });
world.getBodyEvents();          // [{ userData, position, rotation, fellAsleep }]
world.getContactEvents();       // { begin: [...], end: [...], hit: [...] }
world.getSensorEvents();        // { begin: [...], end: [...] }
world.destroy();

Bodies

const body = world.createBody({
  type: 'dynamic',              // 'static' | 'kinematic' | 'dynamic'
  position: { x: 0, y: 5, z: 0 },
  rotation: { x: 0, y: 0, z: 0, w: 1 },
  linearVelocity: { x: 0, y: 0, z: 0 },
  angularDamping: 0.05,
  motionLocks: { angularX: true, angularZ: true },
});

body.getPosition(); body.getRotation(); body.getTransform();
body.setLinearVelocity(v); body.applyLinearImpulseToCenter(v, true);
body.applyForce(force, worldPoint, true); body.applyTorque(t, true);
body.getMass(); body.isAwake(); body.setAwake(true);
body.destroy();

Shapes

Each shape creator takes one options object with the geometry plus material fields (density, friction, restitution, isSensor, filter, event flags):

body.createBox({ halfExtents: { x: 1, y: 0.5, z: 2 }, friction: 0.7 });
body.createSphere({ radius: 0.5, restitution: 0.8 });
body.createCapsule({ height: 1.2, radius: 0.3 });
body.createHull({ points: [{ x, y, z }, ...] });

Joints

Distance, revolute, spherical, prismatic, weld, motor, wheel, parallel, and filter joints are bound:

const hinge = world.createRevoluteJoint(bodyA, bodyB, {
  localFrameA: { position: { x: 0, y: 1, z: 0 } },
  enableMotor: true,
  motorSpeed: 5,
  maxMotorTorque: 100,
});
hinge.getAngle();

Events and userData tags

Every body and shape gets an auto-assigned numeric userData tag (you can overwrite it with your own number). Event arrays reference these tags, so you can map physics events back to your scene objects with a plain Map.

Memory notes

Wrapper objects returned by embind (World, Body, Shape, joints) are tiny handles. Call .delete() when you no longer need the JS handle, and .destroy() to remove the underlying object from the simulation. Destroying a world frees every body, shape, and joint inside it.

Building from source

Requires emsdk (tested with 4.0.18), CMake, and Node 22+.

npm ci
npm run fetch-deps   # clones Box3D at the SHA pinned in scripts/versions.json
npm run build        # builds standard and deluxe flavours into dist/
npm test

License

MIT for the wrapper and build scripts, see LICENSE.

Box3D itself is Copyright (c) Erin Catto and MIT licensed, see LICENSE.box3d.txt and the upstream repository at https://github.com/erincatto/box3d. If you use this package, the physics engine you are running is his work.