Convert Euler Angles to Ammo.js Quaternion

This article provides a straightforward guide on how to reliably convert standard Euler angles (pitch, yaw, and roll) into a valid Ammo.js quaternion. We will explore the native Ammo.js API method, demonstrate a common integration path using Three.js, and highlight key pitfalls like axis ordering and memory management to ensure your 3D physics rotations behave predictably.

The Native Ammo.js Method

Ammo.js (a direct Emscripten port of the Bullet Physics engine) includes a built-in utility on the btQuaternion class designed specifically for Euler conversions: setEulerZYX().

To use this method successfully, you must pass your angles in radians and follow the Yaw-Pitch-Roll convention (which corresponds to rotations around the Y, X, and Z axes respectively).

// 1. Define your rotation angles in radians
const roll  = 0.0; // Rotation around Z-axis
const pitch = Math.PI / 4; // Rotation around X-axis (45 degrees)
const yaw   = Math.PI / 2; // Rotation around Y-axis (90 degrees)

// 2. Instantiate a new Ammo quaternion
const ammoQuaternion = new Ammo.btQuaternion();

// 3. Apply the Euler angles using the ZYX convention
// Note the parameter order: setEulerZYX(yaw, pitch, roll)
ammoQuaternion.setEulerZYX(yaw, pitch, roll);

Understanding the Parameter Order

A common point of failure is passing parameters in the wrong order. The setEulerZYX method expects: 1. First argument: Yaw (rotation around the Y-axis) 2. Second argument: Pitch (rotation around the X-axis) 3. Third argument: Roll (rotation around the Z-axis)


The Three.js Integration Method

Because Ammo.js is frequently paired with Three.js, developers often have rotation data stored in a THREE.Euler object. The most reliable way to convert this is to let Three.js handle the Euler-to-quaternion math, and then copy the resulting values into Ammo.js.

// 1. Create a Three.js Euler angle (XYZ order by default)
const euler = new THREE.Euler(pitch, yaw, roll, 'XYZ');

// 2. Convert Euler to a Three.js Quaternion
const threeQuaternion = new THREE.Quaternion().setFromEuler(euler);

// 3. Create and populate the Ammo.js Quaternion
const ammoQuaternion = new Ammo.btQuaternion(
    threeQuaternion.x,
    threeQuaternion.y,
    threeQuaternion.z,
    threeQuaternion.w
);

This approach is highly reliable because it leverages Three.js’s robust handling of arbitrary rotation orders (such as YXZ or ZYX) before passing the absolute spatial orientation coordinates directly to Ammo.js.


Key Best Practices