How to Apply Impulse in Ammo.js Mathematically

This article explains the mathematically correct way to apply a sudden impulse to an ammo.js rigid body to achieve a precise velocity change. You will learn the underlying physics formulas, how ammo.js handles impulses under the hood, and the exact JavaScript code required to calculate and apply these forces to your 3D physics objects.

The Physics of Impulse

In classical mechanics, an impulse (\(J\)) is defined as the integral of a force over the time interval for which it acts. For a sudden, instantaneous force—like a collision or an explosion—the relationship between impulse, mass (\(m\)), and change in velocity (\(\Delta v\)) is represented by the impulse-momentum theorem:

\[J = m \Delta v\]

To instantly change the velocity of an object by a specific vector (\(\Delta v\)), you must apply an impulse vector equal to the desired change in velocity multiplied by the object’s mass.

Passing a raw velocity vector directly into ammo.js’s impulse methods without multiplying by the mass is a common mistake. Doing so yields inconsistent results for objects of varying weights because heavier objects require more force to change velocity than lighter ones.

Retrieving Mass in Ammo.js

To calculate the correct impulse, you need the object’s mass. However, btRigidBody in ammo.js (and the underlying Bullet Physics engine) does not store the mass directly. Instead, for performance optimization in constraint solving, it stores the inverse mass (\(1/m\)).

You can retrieve the inverse mass using the getInvMass() method. If the object is dynamic (movable), its inverse mass will be greater than zero. The actual mass is calculated as:

\[m = \frac{1}{\text{inverse mass}}\]

If getInvMass() returns 0, the object is static or kinematic, meaning it has infinite mass and cannot be moved by physical impulses.

Step-by-Step Implementation

To mathematically apply a sudden velocity change to a dynamic body in ammo.js, follow these steps:

  1. Define the target velocity change (\(\Delta v\)) as a 3D vector.
  2. Retrieve the inverse mass from the rigid body.
  3. Calculate the mass by inverting the inverse mass.
  4. Multiply the velocity change vector by the mass to get the impulse vector.
  5. Apply the impulse to the rigid body using applyCentralImpulse (for linear motion) or applyImpulse (to apply force at a specific offset, which also generates torque).

Code Example

Here is the mathematically correct implementation in JavaScript using the ammo.js API:

/**
 * Applies a precise velocity change to an ammo.js rigid body.
 * @param {Ammo.btRigidBody} body - The target rigid body.
 * @param {Ammo.btVector3} deltaVelocity - The desired change in velocity.
 */
function applyPreciseImpulse(body, deltaVelocity) {
    const invMass = body.getInvMass();

    // Prevent division by zero for static or kinematic bodies
    if (invMass === 0) {
        console.warn("Cannot apply impulse to a static or kinematic body.");
        return;
    }

    // Calculate actual mass
    const mass = 1.0 / invMass;

    // Calculate impulse vector: J = m * deltaV
    const impulse = new Ammo.btVector3(
        deltaVelocity.x() * mass,
        deltaVelocity.y() * mass,
        deltaVelocity.z() * mass
    );

    // Apply the central impulse (affects linear velocity only)
    body.applyCentralImpulse(impulse);

    // Activate the body to ensure the physics simulation processes the change
    body.activate(true);

    // Clean up allocated Ammo memory
    Ammo.destroy(impulse);
}

Central Impulse vs. Off-Center Impulse

If you want the impulse to rotate the object as well as move it, use applyImpulse instead of applyCentralImpulse. The applyImpulse method accepts two parameters: the impulse vector and a relative position vector (the offset from the center of mass where the force is applied).

// Apply impulse 0.5 units above the center of mass to cause a spin
const relativePosition = new Ammo.btVector3(0, 0.5, 0);
body.applyImpulse(impulse, relativePosition);
Ammo.destroy(relativePosition);