Clean Up and Dispose Ammo.js Physics World

This article provides a step-by-step guide on how to safely and completely dispose of an Ammo.js physics world when transitioning between application scenes. Because Ammo.js is a WebAssembly (Wasm) or Emscripten port of the C++ Bullet Physics library, JavaScript’s automatic garbage collection does not apply to its allocated memory. To prevent severe memory leaks during scene changes, you must manually destroy all rigid bodies, collision shapes, constraints, and the physics world core components themselves.

Why Manual Disposal is Necessary in Ammo.js

Ammo.js operates on a pre-allocated heap within WebAssembly. When you create objects using new Ammo.btRigidBody(...) or new Ammo.btDiscreteDynamicsWorld(...), memory is allocated on this C++ heap. Simply nullifying your JavaScript variables or changing scenes will leave these objects allocated in the Wasm memory, quickly leading to memory exhaustion. You must explicitly call Ammo.destroy() on every allocated object in the correct reverse order of creation.

Step 1: Remove and Destroy Rigid Bodies

Before destroying the physics world, you must remove all rigid bodies currently added to it. You also need to clean up their associated motion states and collision shapes if they are not being reused.

// Assuming you have an array keeping track of your rigid bodies
const rigidBodies = [...]; 

rigidBodies.forEach(body => {
    // Remove the body from the physics world
    physicsWorld.removeRigidBody(body);

    // Destroy the motion state if one was used
    const motionState = body.getMotionState();
    if (motionState) {
        Ammo.destroy(motionState);
    }

    // Destroy the collision shape (if not shared/reused elsewhere)
    const shape = body.getCollisionShape();
    if (shape) {
        Ammo.destroy(shape);
    }

    // Destroy the rigid body itself
    Ammo.destroy(body);
});

// Clear your reference array
rigidBodies.length = 0;

Step 2: Remove and Destroy Constraints and Joints

If your scene utilizes physics constraints (such as hinges, sliders, or point-to-point joints), these must be removed from the world and destroyed.

const joints = [...];

joints.forEach(joint => {
    physicsWorld.removeConstraint(joint);
    Ammo.destroy(joint);
});

joints.length = 0;

Step 3: Dispose of Core Physics World Components

Once all physical entities (bodies, shapes, and constraints) have been removed and destroyed, you can safely dismantle the physics world structure. You must destroy these components in the reverse order of how they were initialized to avoid dangling pointers.

// 1. Destroy the dynamics world
if (physicsWorld) {
    Ammo.destroy(physicsWorld);
    physicsWorld = null;
}

// 2. Destroy the constraint solver
if (solver) {
    Ammo.destroy(solver);
    solver = null;
}

// 3. Destroy the broadphase interface
if (broadphase) {
    Ammo.destroy(broadphase);
    broadphase = null;
}

// 4. Destroy the collision dispatcher
if (dispatcher) {
    Ammo.destroy(dispatcher);
    dispatcher = null;
}

// 5. Destroy the default collision configuration
if (collisionConfiguration) {
    Ammo.destroy(collisionConfiguration);
    collisionConfiguration = null;
}

Best Practices for Scene Transitions