Reset Ammo.js Physics World Without Reloading

Resetting an Ammo.js physics simulation back to its original state is a common requirement for games and interactive 3D applications, allowing players to restart levels instantly without reloading the entire webpage. This article outlines the two most effective methods to completely reset an Ammo.js physics world: recreating the physics world from scratch to clear all cached data, or manually resetting individual rigid bodies to their initial positions and velocities.

The most reliable way to reset Ammo.js is to destroy the existing physics world and instantiate a new one. Ammo.js (a port of the Bullet physics engine) caches collision algorithms, contact points, and broadphase proxies. Simply moving objects can sometimes leave ghost collisions or cached forces behind. Recreating the world guarantees a clean slate.

Step 1: Clean Up Existing Resources

To prevent memory leaks in WebAssembly/C++, you must explicitly destroy all rigid bodies, constraints, and the physics world components using Ammo.destroy().

function cleanupPhysics() {
    // 1. Remove and destroy constraints
    for (let i = constraints.length - 1; i >= 0; i--) {
        const constraint = constraints[i];
        physicsWorld.removeConstraint(constraint);
        Ammo.destroy(constraint);
    }
    constraints = [];

    // 2. Remove and destroy rigid bodies
    for (let i = rigidBodies.length - 1; i >= 0; i--) {
        const body = rigidBodies[i];
        physicsWorld.removeRigidBody(body);
        
        if (body.getMotionState()) {
            Ammo.destroy(body.getMotionState());
        }
        Ammo.destroy(body);
    }
    rigidBodies = [];

    // 3. Destroy the core world infrastructure
    Ammo.destroy(physicsWorld);
    Ammo.destroy(solver);
    Ammo.destroy(overlappingPairCache);
    Ammo.destroy(dispatcher);
    Ammo.destroy(collisionConfiguration);
}

Step 2: Re-initialize the Physics World

After cleanup, call your initialization function to rebuild the physics world and re-populate it with your starting scene objects.

function initPhysics() {
    collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
    dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
    overlappingPairCache = new Ammo.btDbvtBroadphase();
    solver = new Ammo.btSequentialImpulseConstraintSolver();
    physicsWorld = new Ammo.btDiscreteDynamicsWorld(
        dispatcher, 
        overlappingPairCache, 
        solver, 
        collisionConfiguration
    );
    
    // Reset gravity
    physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));
    
    // Recreate your ground, boundaries, and interactive objects here
    createSceneObjects();
}

function resetSimulation() {
    cleanupPhysics();
    initPhysics();
}

Method 2: Manually Resetting Rigid Bodies

If your scene is highly complex and recreating the world causes a noticeable performance stutter, you can manually reset the transform, velocity, and forces of each active rigid body.

To make this work, you must store the initial position, rotation, and mass of every object when it is first created.

Step 1: Clear Forces and Velocities

Simply moving a rigid body is not enough; you must clear its momentum so it does not continue moving with its pre-reset velocity.

function resetRigidBody(body, initialPosition, initialRotation) {
    // 1. Clear forces and velocities
    const zeroVector = new Ammo.btVector3(0, 0, 0);
    body.setLinearVelocity(zeroVector);
    body.setAngularVelocity(zeroVector);
    body.clearForces();

    // 2. Define the initial transform
    const transform = new Ammo.btTransform();
    transform.setIdentity();
    
    const origin = new Ammo.btVector3(initialPosition.x, initialPosition.y, initialPosition.z);
    transform.setOrigin(origin);
    
    const rotation = new Ammo.btQuaternion(
        initialRotation.x, 
        initialRotation.y, 
        initialRotation.z, 
        initialRotation.w
    );
    transform.setRotation(rotation);

    // 3. Apply transform to motion state and world transform
    body.setWorldTransform(transform);
    if (body.getMotionState()) {
        body.getMotionState().setWorldTransform(transform);
    }

    // 4. Force Ammo.js to update the collision bounds
    physicsWorld.updateSingleAabb(body);

    // 5. Activate the body (wakes it up if it went to sleep)
    body.activate(true);

    // Clean up temporary Ammo objects to prevent memory leaks
    Ammo.destroy(zeroVector);
    Ammo.destroy(origin);
    Ammo.destroy(rotation);
    Ammo.destroy(transform);
}

Step 2: Reset the Entire Loop

Iterate through your tracking array and apply the reset function to each object:

function resetAllBodies() {
    for (let i = 0; i < rigidBodies.length; i++) {
        const body = rigidBodies[i];
        const initialState = initialStates[i]; // Object containing saved pos/rot data
        
        resetRigidBody(body, initialState.position, initialState.rotation);
    }
}

Using Method 1 is highly recommended for standard games to guarantee that collision registers, solver constraints, and contact manifolds are entirely wiped out, preventing physics bugs upon restart. Use Method 2 only if real-time constraints require a sub-millisecond reset loop.