How to Apply Local Force in Ammo.js

When developing physics-based simulations or games using the ammo.js physics engine, you frequently need to apply forces relative to an object’s local orientation—such as pushing a spaceship forward along its own nose direction. Because ammo.js does not feature a built-in “applyLocalForce” method, you must manually transform your local force vector into a world-space vector before applying it. This article details the primary, most efficient methods used to convert local forces to world forces in ammo.js, complete with step-by-step code implementations.

The Core Concept

In ammo.js, methods like applyCentralForce() and applyForce() expect vectors defined in world coordinates. If you apply a static vector like (0, 0, 10) directly using these methods, the force will always push the object toward the world’s positive Z-axis, regardless of which way the object is facing.

To apply a local force (e.g., forward thrust relative to the object’s current rotation), you must rotate the local force vector by the object’s current world rotation quaternion.


The cleanest and most efficient way to apply a local force is to use the native helper function Ammo.quatRotate(). This function takes the rigid body’s rotation quaternion and uses it to rotate your local vector into world space.

Step-by-Step Implementation

  1. Define the local force vector representing the direction and magnitude relative to the object.
  2. Retrieve the object’s current world transform and extract its rotation quaternion.
  3. Rotate the local vector into world space using Ammo.quatRotate().
  4. Apply the resulting world vector to the rigid body.
  5. Clean up memory to prevent memory leaks.

Code Example

// 1. Define the local force (e.g., 10 units of force along the local Z-axis)
var localForce = new Ammo.btVector3(0, 0, 10);

// 2. Get the rigid body's current world transform and rotation
var transform = rigidBody.getWorldTransform();
var rotation = transform.getRotation(); // Returns a btQuaternion

// 3. Rotate the local force vector into world space
var worldForce = Ammo.quatRotate(rotation, localForce);

// 4. Apply the converted force to the center of the rigid body
rigidBody.applyCentralForce(worldForce);

// 5. Free up memory allocated in the ammo.js heap
Ammo.destroy(localForce);
Ammo.destroy(worldForce);

Method 2: Transformation via Matrix Multiplication

Another approach is to multiply the local force vector by the rigid body’s basis (the 3x3 rotation matrix extracted from its world transform). While slightly more verbose, this method is useful if you are already manipulating transform matrices.

Code Example

// 1. Define the local force vector
var localForce = new Ammo.btVector3(0, 0, 10);

// 2. Get the object's world basis (rotation matrix)
var transform = rigidBody.getWorldTransform();
var basis = transform.getBasis(); // Returns a btMatrix3x3

// 3. Multiply the basis matrix by the local vector to get the world force
var worldForce = basis.op_mul(localForce); 

// 4. Apply the force
rigidBody.applyCentralForce(worldForce);

// 5. Clean up allocated Ammo objects
Ammo.destroy(localForce);
Ammo.destroy(worldForce);

Applying Local Force at an Offset Position

If you want to apply a local force at a specific offset from the center of mass (to generate torque and cause rotation, like a thruster mounted on the wing of an airplane), you must transform both the force vector and the offset position vector into world space.

var localForce = new Ammo.btVector3(0, 0, 10);
var localOffset = new Ammo.btVector3(2, 0, 0); // Mounted 2 units to the right

var transform = rigidBody.getWorldTransform();
var rotation = transform.getRotation();

// Transform both vectors to world coordinates
var worldForce = Ammo.quatRotate(rotation, localForce);
var worldOffset = Ammo.quatRotate(rotation, localOffset);

// Apply force at the offset relative to the center of mass
rigidBody.applyForce(worldForce, worldOffset);

// Memory cleanup
Ammo.destroy(localForce);
Ammo.destroy(localOffset);
Ammo.destroy(worldForce);
Ammo.destroy(worldOffset);

Important Memory Note

Because ammo.js is a WebAssembly/Emscripten port of C++, objects created with new Ammo.btVector3() are allocated on the C++ heap. To prevent severe memory leaks in your animation loops, always destroy your temporary vectors using Ammo.destroy(vector) once the force has been applied.