JoltPhysics.js: a WebAssembly build of the Jolt physics engine
Port of JoltPhysics to JavaScript using emscripten
At a glance
- What is it?
- JoltPhysics.js compiles the Jolt Physics rigid body engine to WebAssembly with Emscripten and publishes it on npm as jolt-physics, with seven build flavours covering embedded WASM, separate WASM, asm.js, multithreading and debug checking. The interface matches the C++ one, so the Jolt documentation is the reference, and the cost of that convenience is manual memory management.
- Who is it for?
- Adopt jolt-physics if you are building a browser game or VR application that needs a real rigid body solver and want Jolt specifically, and you are prepared to destroy the objects you allocate. Do not adopt it if you need a physics engine with a managed-memory JavaScript API, since the WebAssembly boundary is manual.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The C++ documentation is the API documentation
JoltPhysics.js enables using Jolt Physics in JavaScript, and the design decision that makes the whole thing usable is stated in one line. The interface of the library is the same as the C++ interface of JoltPhysics, which means you can use the C++ documentation as reference.
That is a larger deal than it sounds. Jolt is a large C++ library with a substantial API surface, and the usual cost of a WebAssembly port is that you get a thin and slightly awkward wrapper while the real documentation stays in C++ terms. Here, the wrapper is the interface, so the C++ docs apply directly.
The README says almost the entire Jolt interface has been exposed, and it points to a specific file as the source of truth for that claim. `JoltJS.idl` in the repository root is the WebIDL description of what is exposed. If an interface is missing, the instruction is to edit `JoltJS.idl` and `JoltJS.h` and send a pull request, or open an issue. The repository also has a `JoltJS-DebugRenderer.idl` and a `JoltJS-DebugRenderer.h`, which is how the on-screen debug renderer comes across the boundary.
The examples directory shows the range of what is covered. There are files for 2d_funnel, add_remove_bodies, alternative_collision_filtering and its raycast variant, buoyancy, character_virtual, constraint_path, constraint_pulley, constraint_svg_path, constraints, contact_listener, conveyor_belt with a threaded variant, falling_shapes, friction, heightfield, motor, proper_cleanup, ray_cast, render-debug, and a rig directory. Vehicles, soft bodies, characters and ragdolls are all represented, which is the signal that this is a port of the full engine rather than a subset.
The demos page at jrouwe.github.io/JoltPhysics.js is where the project shows itself in action.
Seven flavours, and the difference is where the WASM file lives
The library ships in seven variants, and choosing between them is the first decision a user makes.
`wasm-compat` is a WASM version with the WASM file encoded in base64 and embedded in the bundle. `debug-wasm-compat` is the same with debug checking enabled, which outputs errors to the console and enables the debug renderer. `wasm` is a WASM version with a separate WASM file. `asm` is a JavaScript version using asm.js.
Then the multithreaded family. `wasm-compat-multithread` and `debug-wasm-compat-multithread` are the compat versions with multi threading enabled, and `wasm-multithread` is the separate-file version with multi threading.
The distinction that matters most in practice is embedded versus separate. The compat variants are self-contained, so a single script tag works with no asset hosting. The plain WASM variants keep the binary as its own file, which keeps the bundle small, but then you have to tell the library where the file lives. The README explains the requirement precisely: to use the `wasm` flavour you must either serve the WASM file `jolt-physics.wasm.wasm` alongside `jolt-physics.wasm.js`, or use a bundler that supports importing an asset as a url, and then tell Jolt where to find the file.
That is done with a `locateFile` function passed to the default export, and the README gives the vite example:
import initJolt from "jolt-physics";
import joltWasmUrl from "jolt-physics/jolt-physics.wasm.wasm?url";
const Jolt = await initJolt({
locateFile: () => joltWasmUrl,
});Note that this form is asynchronous, since the module has to fetch the binary before it can be used. The `locateFile` mechanism is the standard Emscripten one, and the README points at the Emscripten documentation for the details.
Installation is an npm install plus an entrypoint choice
The library is distributed as ECMAScript modules on npm, and the install is a single command:
npm install jolt-physicsThe different flavours are then available as entrypoints on the same package, and the README lists all seven imports:
// WASM embedded in the bundle
import Jolt from 'jolt-physics';
import Jolt from 'jolt-physics/wasm-compat';
// WASM embedded in the bundle, debug checking enabled (outputs errors to the console and enables the debug renderer)
import Jolt from 'jolt-physics/debug-wasm-compat';
// WASM
import Jolt from 'jolt-physics/wasm';
// asm.js
import Jolt from 'jolt-physics/asm';The package manifest confirms the mapping. The root export points at `dist/jolt-physics.wasm-compat.js` with matching type declarations, and each subpath has its own entry, with the multithread variants pointing at `dist/jolt-physics.multithread.wasm.js` and `dist/jolt-physics.multithread.wasm-compat.js` respectively. The WASM binaries are exposed as exports too, under `./jolt-physics.wasm.wasm` and `./jolt-physics.multithread.wasm.wasm`.
There is also a route that skips the bundler entirely. The README shows importing the ESM bundles from unpkg:
<script type="module">
// import latest
import Jolt from 'https://www.unpkg.com/jolt-physics/dist/jolt-physics.wasm-compat.js';
// or import a specific version
import Jolt from 'https://www.unpkg.com/[email protected]/dist/jolt-physics.wasm-compat.js';
</script>with x.y.z standing in for the version you want. That is the fastest way to get a Jolt world running in a page without any build step, and the pinned-version form is what you want for anything you intend to ship.
The npm package is `jolt-physics` at version 1.1.0, described as a WebAssembly port of JoltPhysics, a rigid body physics and collision detection library, suitable for games and VR applications.
Memory is manual, and the README is blunt about it
The Memory Management section is the part of the documentation that tells you what the port costs you, and it is worth reading before you write any code.
The samples are described as very bad at cleaning up after themselves, basically they do not clean up. The reason given is that when using Emscripten to port a library to WASM, nothing is cleaned up automatically, so everything allocated with `new Jolt.XXX` has to be destroyed with `Jolt.destroy(...)`.
On top of that, Jolt uses reference counting for a number of its classes, and everything that inherits from RefTarget is one of them. The README lists the most important ones, and it is a long list: ShapeSettings, Shape, ConstraintSettings, Constraint, PathConstraintPath, PhysicsMaterial, GroupFilter, SoftBodySharedSettings, VehicleCollisionTester, VehicleControllerSettings, WheelSettings, CharacterBaseSettings, CharacterBase, Skeleton, SkeletonAnimation, SkeletonMapper, PhysicsScene, RagdollSettings and Ragdoll.
That list explains the shape of the port. A C++ engine with reference-counted smart pointers assumes deterministic lifetime management, and a WebAssembly boundary with a manual heap does not give you that for free. Objects that inherit from RefTarget start with a reference count of 0, and the README continues into how to take ownership, but the section is cut off in the copy available here.
There is an example named `proper_cleanup.html` in the Examples directory, which is the most direct way to learn the intended pattern. And there is a `helpers.js` at the repository root alongside `front-matter.js` and `multi-threaded.js`, which are the kind of small support modules a WASM port needs to make the boundary tolerable.
Building it yourself needs Emscripten, cmake and a Linux host
The Building section opens with a constraint that eliminates most contributors: this project has only been compiled under Linux.
The requirements are then short. Install emscripten and make sure its environment variables are set up, install cmake, and run `./build.sh` to build both the Debug and the Distribution build, or `./build.sh Debug` for only the Debug build.
The additional build options are where the interesting trade-offs live, and each one maps to a real constraint.
`-DENABLE_MEMORY_PROFILER=ON` enables memory tracking to detect leaks. `-DDOUBLE_PRECISION=ON` enables double precision mode, which allows worlds larger than a couple of km, so the default single precision is a deliberate size limit rather than an oversight. `-DENABLE_SIMD=ON` enables SIMD instructions, and the README notes that Safari 16.4 was the last major browser to support this, in March 2023, and that the multithreaded builds have it on by default. That last detail matters: a multithreaded build will not run on a current Safari.
`-DBUILD_WASM_COMPAT_ONLY=ON` speeds up the build by only compiling the WASM compat version, which the examples use. And `-DCROSS_PLATFORM_DETERMINISTIC=ON` builds the library so that it produces the same results as the native version of the library, with a link to the Jolt documentation on deterministic simulation.
That last option is the one to enable if you care about reproducibility, and it is the one most projects forget to turn on. The repository top level shows one directory per flavour, `wasm/`, `wasm-compat/`, `wasm-multithread/`, `wasm-compat-multithread/`, `debug-wasm-compat/`, `debug-wasm-compat-multithread/` and `asm/`, which is the build matrix materialized as directories, plus `ci/`, `serveexamples/` and `helloworld/`.
Running the examples needs a web server, not a file open
The Running section contains a detail that trips people up. By default the examples use the WASM compat version of Jolt, and that requires serving the html file using a web server rather than opening the html file directly.
The reason is inherent to the compat flavour. The WASM binary is embedded in the bundle as base64, but the module still has to be fetched and instantiated, and browsers block that for a file loaded from the local filesystem. A double click on an example html file is therefore not enough.
The instructions are a terminal in the project directory and two commands:
npm install
npm run examplesand then the examples are at http://localhost:3000/. The `examples` script in the manifest runs `node serveexamples/serveexamples.cjs`, which is the `serveexamples/` directory at the repository root.
For debugging the C++ side, the README points at Chrome's WASM debugging documentation, which is what you need when the JavaScript wrapper is behaving but the underlying engine is not.
One more thing to note in the top level entries. There is a `coi-serviceworker.min.js` in the Examples directory, which is a service worker that sets the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers. Those headers are what allow `SharedArrayBuffer` to be used, and `SharedArrayBuffer` is what the multithreaded WebAssembly builds need. So the multithreaded flavours do not just need a modern browser, they need the page to be served with the right isolation headers, and the examples ship the service worker that does it.
Compared with a native physics engine and with a JavaScript-native solver
Two alternatives exist for browser physics, and each gives up something specific.
A native WebAssembly physics engine written for the web, with a managed or arena-allocated object model and a JavaScript-friendly API, avoids the entire memory management problem described above. Its cost is that it is not Jolt, so if your project has a native C++ component or a team that already knows Jolt, you are switching engines. Jolt has a large body of existing knowledge, tuning knowledge in particular, and a Jolt build on the desktop and a Jolt build in the browser share that knowledge. The port's payoff is exactly that: the C++ documentation applies, and the engine is the same one.
A pure JavaScript solver, the kind written in TypeScript with objects and garbage collection, is easier to debug and easier to inspect in a devtools console, and it has no boundary crossing at all. The cost is raw performance and, more importantly, a smaller feature set. Jolt covers vehicles, soft bodies, characters with virtual joints, ragdolls, constraints and path constraints, and reimplementing that is not a weekend project. A JavaScript-native solver will usually be a much smaller engine, and the difference shows up in the games you can attempt.
The multithreaded variants are the third option, and they are the most interesting choice. A separate WASM file with threads and SIMD runs the solver across cores in the browser, which is the closest this project gets to native performance. The cost is the isolation header requirement, the Safari SIMD limitation, and the loss of the single-file simplicity that the compat flavour buys.
Editorial conclusion
Adopt jolt-physics if you are building a browser game or VR application that needs a real rigid body solver and want Jolt specifically, and you are prepared to destroy the objects you allocate. Do not adopt it if you need a physics engine with a managed-memory JavaScript API, since the WebAssembly boundary is manual. Verify first which flavour your deployment can actually load, because the compat variants embed the WASM as base64 while the plain WASM variants require you to serve the file or configure `locateFile`, and confirm that cross-platform determinism is on if your simulation needs matching results across machines.
Frequently asked questions
What is JoltPhysics.js and what does it provide?
It is a WebAssembly port of Jolt Physics, a rigid body physics and collision detection library, built with Emscripten and published on npm as jolt-physics. The interface is the same as the C++ interface of JoltPhysics, so the C++ documentation serves as the reference, and the README says almost the entire Jolt interface has been exposed.
How do I install and import jolt-physics?
Run `npm install jolt-physics`, then import the flavour you want from the package entrypoints, for example `import Jolt from 'jolt-physics'` for the compat build or `import Jolt from 'jolt-physics/wasm'` for the separate WASM file version. The library is distributed as ECMAScript modules.
What is the difference between the wasm and wasm-compat flavours?
The wasm-compat flavour has the WASM file encoded in base64 and embedded in the bundle, while the wasm flavour keeps it as a separate file. For the separate-file version you must either serve the WASM file alongside the JS file, or pass a `locateFile` function to the default export so the library knows where to find it.
How do I free memory when using jolt-physics?
Nothing is cleaned up automatically across the WebAssembly boundary, so everything created with `new Jolt.XXX` has to be destroyed with `Jolt.destroy(...)`. On top of that, Jolt uses reference counting for classes inheriting from RefTarget, including Shape, Constraint, PhysicsScene, Ragdoll and many settings classes, and the README warns the samples do not clean up after themselves.
Can I build JoltPhysics.js myself?
Yes, but the README says the project has only been compiled under Linux. You need emscripten with its environment variables set up plus cmake, then run `./build.sh`, or `./build.sh Debug` for the Debug build only, with options such as `-DDOUBLE_PRECISION=ON` for larger worlds and `-DCROSS_PLATFORM_DETERMINISTIC=ON` for results matching the native library.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jrouwe-joltphysics-js)Was this useful?