Three.js Rotation: Euler Angles vs Quaternions

Rotating 3D objects in Three.js can be achieved using two primary mathematical representations: Euler angles and Quaternions. While Euler angles are intuitive and easy to understand for simple rotations, they suffer from mathematical limitations like gimbal lock. Quaternions, on the other hand, provide a robust, albeit mathematically complex, solution that avoids gimbal lock and enables smooth interpolation. This article compares how Three.js utilizes both methods, highlights their key differences, and explains when to use each approach for optimal 3D scene development.

Euler Angles in Three.js

Euler angles represent a rotation by rotating an object around three distinct axes (X, Y, and Z) in a specific sequence. In Three.js, every Object3D has a rotation property which is an instance of the Euler class.

How It Works

You rotate an object by directly modifying its axial properties in radians:

// Rotate 45 degrees (PI / 4 radians) around the Y axis
mesh.rotation.y = Math.PI / 4;

The Ordering Issue and Gimbal Lock

The order in which these rotations are applied matters. By default, Three.js applies rotations in the ‘XYZ’ order. You can change this using:

mesh.rotation.order = 'YXZ';

Because rotations are applied sequentially, rotating an object 90 degrees around one axis can align the other two axes. This causes a loss of one degree of freedom, a phenomenon known as gimbal lock. When gimbal lock occurs, the object can no longer rotate along one of its expected axes, causing unexpected snapping or erratic movement during animations.


Quaternions in Three.js

Quaternions represent rotations in a four-dimensional space using four values: x, y, z, and w. Instead of rotating sequentially around three axes, a quaternion represents a single rotation around a specified 3D vector (axis) by a specific angle. In Three.js, every Object3D has a quaternion property.

How It Works

Because four-dimensional numbers are highly non-intuitive to write manually, Three.js provides helper methods to work with the Quaternion class:

const quaternion = new THREE.Quaternion();
const axis = new THREE.Vector3(0, 1, 0); // Rotate around the Y axis
const angle = Math.PI / 4; // 45 degrees

quaternion.setFromAxisAngle(axis, angle);
mesh.quaternion.copy(quaternion);

Advantages of Quaternions


Synchronization in Three.js

Three.js automatically keeps Euler angles and Quaternions synchronized. By default, when you update mesh.rotation (Euler), Three.js automatically updates mesh.quaternion behind the scenes, and vice versa.

If you want to manipulate quaternions directly and prevent automatic overrides, Three.js manages this state internally. However, if you are doing complex manual matrix updates, you can disable automatic updates or force synchronization using:

mesh.rotation.onChangeCallback(); 
// Or manually copy one representation to another:
mesh.quaternion.setFromEuler(mesh.rotation);

Key Differences and Use Cases

Feature Euler Angles (mesh.rotation) Quaternions (mesh.quaternion)
Data Structure 3 values: x, y, z (angles in radians) 4 values: x, y, z, w
Intuitiveness High (easy to read and tweak manually) Low (requires helper methods)
Gimbal Lock Yes, susceptible No, completely immune
Interpolation Jagged, unpredictable over long transitions Smooth, linear transitions (SLERP)

When to Use Euler Angles

When to Use Quaternions