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:
- Define the target velocity change (\(\Delta v\)) as a 3D vector.
- Retrieve the inverse mass from the rigid body.
- Calculate the mass by inverting the inverse mass.
- Multiply the velocity change vector by the mass to get the impulse vector.
- Apply the impulse to the rigid body using
applyCentralImpulse(for linear motion) orapplyImpulse(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);