Transforming Objects in 3D Space
Every object in Three.js has three fundamental properties that control where it is and how it appears: position, rotation, and scale. These are all inherited from Object3D, so they work the same way for meshes, groups, lights, and cameras.
Position
Position is a Vector3 that defines where an object sits in 3D space. The three axes are:
- X - Left/Right (positive = right)
- Y - Up/Down (positive = up)
- Z - Forward/Back (positive = toward camera)
// Set position properties individually
mesh.position.x = 2;
mesh.position.y = 1;
mesh.position.z = -3;
// Or use the set() method
mesh.position.set(2, 1, -3);
// Position is a Vector3, so you can use vector methods
mesh.position.copy(otherMesh.position);
mesh.position.add(new THREE.Vector3(1, 0, 0));
mesh.position.multiplyScalar(2);
// Get distance from another point
const distance = mesh.position.distanceTo(camera.position);
// Get length (distance from origin)
const length = mesh.position.length();Rotation
Rotation uses Euler angles by default, which are three values representing rotation around each axis, measured in radians (not degrees). A full rotation is Math.PI * 2 (approximately 6.28). If you need a refresher on Radians, I highly suggest this video on Khan Academy for what they are and why we use them instead of Degrees.
// Rotate around each axis (in radians)
mesh.rotation.x = Math.PI / 4; // 45 degrees
mesh.rotation.y = Math.PI / 2; // 90 degrees
mesh.rotation.z = Math.PI; // 180 degrees
// Use set() for all at once
mesh.rotation.set(0, Math.PI / 4, 0);
// Convert degrees to radians
const degrees = 45;
mesh.rotation.y = THREE.MathUtils.degToRad(degrees);
// Rotation order matters! Default is 'XYZ'
mesh.rotation.order = 'YXZ'; // Rotate Y first, then X, then ZThe order that rotations are applied matters. If you're getting unexpected results, try changing rotation.order. For character controllers, 'YXZ' is often more intuitive.
Quaternions
For complex rotations or smooth interpolation, use quaternions instead of Euler angles. They avoid "gimbal lock" (where axes align and you lose a degree of freedom):
// Set rotation using quaternion
mesh.quaternion.setFromAxisAngle(
new THREE.Vector3(0, 1, 0), // axis to rotate around
Math.PI / 4 // angle in radians
);
// Smoothly interpolate between rotations
mesh.quaternion.slerp(targetQuaternion, 0.1);
// Look at a point (uses quaternion internally)
mesh.lookAt(0, 0, 0);
mesh.lookAt(targetMesh.position);If you're new to Quaternions, I highly suggest this 3Blue1Brown video.
Scale
Scale multiplies the size of an object along each axis. A scale of 1 is the original size, 2 is double, 0.5 is half.
// Scale each axis independently
mesh.scale.x = 2; // Stretch horizontally
mesh.scale.y = 0.5; // Squash vertically
mesh.scale.z = 1; // Keep depth the same
// Uniform scaling (same on all axes)
mesh.scale.set(1.5, 1.5, 1.5);
// or
mesh.scale.setScalar(1.5);
// Scale is also a Vector3
mesh.scale.multiplyScalar(2); // Double the current scaleCombining Transforms
You can combine all three transforms on a single object. Internally, Three.js combines these into a transformation matrix and saves you the headache of doing this manually, one by one.
// All transforms together
mesh.position.set(2, 0, 0);
mesh.rotation.set(0, Math.PI / 4, 0);
mesh.scale.set(1, 2, 1);
// The order of operations is always:
// 1. Scale
// 2. Rotate
// 3. Translate (position)
// Force matrix update (usually automatic)
mesh.updateMatrix();
// Access the combined transformation matrix
console.log(mesh.matrix);Helper Methods
Three.js provides convenient methods for relative transforms and when possible, you should use them.
// Translate relative to current position
mesh.translateX(1); // Move 1 unit along local X axis
mesh.translateY(2); // Move 2 units along local Y axis
mesh.translateZ(-1); // Move 1 unit along local Z axis
// These respect the object's rotation!
// translateZ moves forward relative to where the object is facing
// Rotate relative to current rotation
mesh.rotateX(0.1);
mesh.rotateY(0.1);
mesh.rotateZ(0.1);
// Rotate around world axis (not local)
mesh.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), 0.1);
// Point at something
mesh.lookAt(target.position);
camera.lookAt(scene.position);Local vs World Space
Local space defines an object's position relative to its parent, meaning if you move a car, the steering wheel’s local coordinates remain unchanged. World space is the "big picture" view, representing an object's absolute location relative to the scene’s origin (0,0,0) regardless of its parentage.
You typically use local space for internal motions like a character’s arm swinging, while world space is essential for global logic like physics and collision detection. Since Three.js defaults to local coordinates for its .position property, you must use methods like .getWorldPosition() to find an object's true location in the universe.
To get or set world-space values:
// Get world position (accounting for all parents)
const worldPos = new THREE.Vector3();
mesh.getWorldPosition(worldPos);
// Get world rotation
const worldQuat = new THREE.Quaternion();
mesh.getWorldQuaternion(worldQuat);
// Get world scale
const worldScale = new THREE.Vector3();
mesh.getWorldScale(worldScale);
// Convert between local and world
const localPoint = new THREE.Vector3(1, 0, 0);
const worldPoint = mesh.localToWorld(localPoint.clone());
const backToLocal = mesh.worldToLocal(worldPoint.clone());