How to Calculate Sliding Velocity in Ammo.js

This article explains how to determine the relative sliding (tangential) velocity between two intersecting or colliding physical bodies using the Ammo.js physics engine. You will learn how to access contact manifolds, extract collision normals, calculate the velocities of the bodies at the exact contact point, and project these velocities to isolate the sliding speed.

To find the sliding velocity of two intersecting objects in Ammo.js, you must analyze the contact points generated during the physics simulation step. Sliding velocity is the relative velocity of the two bodies at the contact point, projected onto the plane tangent to the collision normal.

Step 1: Access Collision Manifolds

After running the physics step (dynamicsWorld.stepSimulation), iterate through the collision dispatcher to find active contact manifolds.

const dispatcher = dynamicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();

for (let i = 0; i < numManifolds; i++) {
    const manifold = dispatcher.getManifoldByIndexInternal(i);
    const numContacts = manifold.getNumContacts();
    
    if (numContacts === 0) continue;

    const body0 = Ammo.castObject(manifold.getBody0(), Ammo.btRigidBody);
    const body1 = Ammo.castObject(manifold.getBody1(), Ammo.btRigidBody);

    for (let j = 0; j < numContacts; j++) {
        const contactPoint = manifold.getContactPoint(j);
        
        // Only process actual intersections
        if (contactPoint.getDistance() < 0) {
            calculateSlidingVelocity(body0, body1, contactPoint);
        }
    }
}

Step 2: Get Point Velocities

Objects in a physics engine can rotate, meaning the velocity at the specific contact point is a combination of both linear and angular velocity. You must calculate the total velocity for both bodies at the contact position.

Using the contact point’s world position, calculate the relative position vector from each body’s center of mass, then retrieve the velocity at that point.

function calculateSlidingVelocity(body0, body1, contactPoint) {
    // 1. Get the contact point in world coordinates
    const pointOnA = contactPoint.getPositionWorldOnA(); // btVector3
    const pointOnB = contactPoint.getPositionWorldOnB(); // btVector3
    
    // Average position representing the contact point
    const contactPos = new Ammo.btVector3(
        (pointOnA.x() + pointOnB.x()) * 0.5,
        (pointOnA.y() + pointOnB.y()) * 0.5,
        (pointOnA.z() + pointOnB.z()) * 0.5
    );

    // 2. Compute relative vectors from center of mass
    const relPosA = contactPos.op_sub(body0.getWorldTransform().getOrigin());
    const relPosB = contactPos.op_sub(body1.getWorldTransform().getOrigin());

    // 3. Get total velocity at the contact point for both bodies
    const velA = body0.getVelocityInLocalPoint(relPosA);
    const velB = body1.getVelocityInLocalPoint(relPosB);
    
    // 4. Calculate relative velocity vector
    const relativeVelocity = velA.op_sub(velB);

    // Clean up temporary vectors to prevent memory leaks in WebAssembly
    Ammo.destroy(relPosA);
    Ammo.destroy(relPosB);
    Ammo.destroy(velA);
    Ammo.destroy(velB);
    Ammo.destroy(contactPos);
    
    computeTangentialComponent(relativeVelocity, contactPoint);
}

Step 3: Project Velocity onto the Tangent Plane

The relative velocity vector contains both the velocity along the collision normal (impact/separation speed) and the velocity perpendicular to the normal (sliding speed). To isolate the sliding velocity, subtract the normal component from the total relative velocity.

function computeTangentialComponent(relativeVelocity, contactPoint) {
    const normal = contactPoint.m_normalWorldOnB; // btVector3 pointing from B to A

    // Calculate normal velocity component (projection of relative velocity onto normal)
    const normalVelMag = relativeVelocity.dot(normal);
    
    const normalVelocity = new Ammo.btVector3(
        normal.x() * normalVelMag,
        normal.y() * normalVelMag,
        normal.z() * normalVelMag
    );

    // Subtract normal velocity from relative velocity to get tangent (sliding) velocity
    const slidingVelocityVector = relativeVelocity.op_sub(normalVelocity);
    
    // The magnitude of this vector is the sliding speed (m/s)
    const slidingSpeed = slidingVelocityVector.length();

    console.log(`Sliding Velocity: ${slidingSpeed} m/s`);

    // Clean up WebAssembly pointers
    Ammo.destroy(relativeVelocity);
    Ammo.destroy(normalVelocity);
    Ammo.destroy(slidingVelocityVector);
}