Run Multiple Ammo.js Worlds Simultaneously
Yes, it is entirely possible to run two or more completely independent Ammo.js physics worlds simultaneously in the same application. This article explains how to achieve this by instantiating separate sets of core Bullet Physics components, allowing you to isolate environments for tasks such as UI physics, gameplay physics, client-side prediction, or parallel simulations.
How to Create Isolated Physics Worlds
Ammo.js is a direct port of the C++ Bullet Physics library to JavaScript/WebAssembly. To create a physics world, you must instantiate a series of helper objects that manage collisions, broadphase algorithms, and constraint solvers.
To run two separate worlds, you simply need to instantiate two unique sets of these core components. They will not share memory, collision events, or rigid bodies.
Here is the structural setup required for two independent worlds:
// World 1 Setup
const collisionConfiguration1 = new Ammo.btDefaultCollisionConfiguration();
const dispatcher1 = new Ammo.btCollisionDispatcher(collisionConfiguration1);
const overlappingPairCache1 = new Ammo.btDbvtBroadphase();
const solver1 = new Ammo.btSequentialImpulseConstraintSolver();
const physicsWorld1 = new Ammo.btDiscreteDynamicsWorld(
dispatcher1,
overlappingPairCache1,
solver1,
collisionConfiguration1
);
physicsWorld1.setGravity(new Ammo.btVector3(0, -9.8, 0));
// World 2 Setup (Entirely Separate)
const collisionConfiguration2 = new Ammo.btDefaultCollisionConfiguration();
const dispatcher2 = new Ammo.btCollisionDispatcher(collisionConfiguration2);
const overlappingPairCache2 = new Ammo.btDbvtBroadphase();
const solver2 = new Ammo.btSequentialImpulseConstraintSolver();
const physicsWorld2 = new Ammo.btDiscreteDynamicsWorld(
dispatcher2,
overlappingPairCache2,
solver2,
collisionConfiguration2
);
physicsWorld2.setGravity(new Ammo.btVector3(0, 0, 0)); // Can have different gravityManaging Bodies and stepping the Simulations
Rigid bodies, collision shapes, and constraints belong strictly to
the world they are added to. A btRigidBody added to
physicsWorld1 via addRigidBody() will have no
physical interaction with any body inside
physicsWorld2.
To keep both simulations running, you must step each world independently inside your main animation or update loop:
function update(deltaTime) {
// Step World 1 (e.g., standard gameplay)
physicsWorld1.stepSimulation(deltaTime, 10);
// Step World 2 (e.g., background prediction or UI elements)
physicsWorld2.stepSimulation(deltaTime, 10);
}Memory Management Considerations
Because Ammo.js is a WebAssembly wrapper, it does not support automatic garbage collection for its C++ objects. You must manually destroy all instantiated objects to prevent memory leaks.
When tearing down your application or resetting a specific world,
ensure you call Ammo.destroy() on all unique components
associated with that specific world instance, including the dispatcher,
broadphase, solver, collision configuration, and the dynamics world
itself.