Understanding Transformations in Three.js

Transformations are the foundation of all 3D movement and animation. Whether you're building a game, creating an architectural visualization, or designing an interactive product showcase, understanding how to position, rotate, and scale objects is essential.
In this post, we'll explore the three core transformations in Three.js and how they work together to bring your 3D scenes to life.
The Three Core Transformations
Every object in Three.js has three fundamental properties that define its transformation:
Position: Where the object is located in 3D space
Rotation: How the object is oriented or tilted
Scale: How large or small the object appears
Think of a toy car on a table:
Position is where you place it on the table (left, right, forward, back, up, down)
Rotation is which direction it's facing and whether it's tilted
Scale is like using a magnifying glass making it appear bigger or smaller
Transform Order
Three.js applies these transformations in a specific order internally:
Scale → Rotation → Position
You cannot change this order. Understanding this sequence is important because it affects how your objects behave. For example, if you scale an object after rotating it, the rotation axis itself gets scaled, which can produce unexpected results.
Position: Moving Objects in 3D Space
Position determines where an object exists in 3D space using three coordinates: X, Y, and Z.
X axis: Left (negative) to Right (positive)
Y axis: Down (negative) to Up (positive)
Z axis: Into screen (negative) to Out toward you (positive)
Positioning a Mesh
import * as THREE from "three";
const canvas = document.querySelector("#canvas");
// Scene setup
const scene = new THREE.Scene();
// Camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 5);
// Renderer
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
// Create a cube
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x44aa88,
emissive: 0x112244,
});
const cube = new THREE.Mesh(boxGeometry, material);
scene.add(cube);
// Position the cube
cube.position.set(2, 1, 0); // x=2 (right), y=1 (up), z=0 (center)
// Or set individually
// cube.position.x = 2;
// cube.position.y = 1;
// cube.position.z = 0;
// Add lighting
const light = new THREE.DirectionalLight(0xffffff, 5);
light.position.set(5, 5, 5);
scene.add(light);
// Render
renderer.render(scene, camera);
The position property is a Vector3 object with x, y, and z components. You can set all three at once with position.set() or individually.
Rotation: Orienting Objects
Rotation determines which direction an object faces and how it's tilted. In Three.js, rotations are measured in radians, not degrees.
Quick conversion:
360° = 2π radians (full circle)
180° = π radians
90° = π/2 radians
Rotating a Mesh
// Rotate around each axis (in radians)
cube.rotation.x = Math.PI / 4; // 45 degrees around X axis
cube.rotation.y = Math.PI / 2; // 90 degrees around Y axis
cube.rotation.z = 0; // No rotation around Z axis
// Or convert from degrees
function degreesToRadians(degrees) {
return degrees * (Math.PI / 180);
}
cube.rotation.y = degreesToRadians(90); // 90 degrees
Animated Rotation
function animate() {
requestAnimationFrame(animate);
// Increment rotation each frame for smooth animation
cube.rotation.x += 0.01; // Slow rotation around X
cube.rotation.y += 0.005; // Even slower around Y
renderer.render(scene, camera);
}
animate();
Important: When you increment rotation values in an animation loop (+=), the values accumulate every frame. This creates continuous spinning motion.
Scale: Resizing Objects
Scale controls the size of an object along each axis. A scale of 1 is the original size, 2 doubles the size, and 0.5 halves it.
Scaling a Mesh
// Scale uniformly (all axes the same)
cube.scale.set(2, 2, 2); // Double the size
// Or scale non-uniformly (different on each axis)
cube.scale.set(1.5, 1, 0.5); // Wider, same height, thinner
// Individual axis scaling
cube.scale.x = 1.5; // 150% width
cube.scale.y = 1.0; // Original height
cube.scale.z = 0.5; // 50% depth
Non-uniform scaling (different values on different axes) is useful for creating stretched or squashed effects, like making a sphere into an egg shape.
Complete Basic Example
Here's a complete example demonstrating all three transformations:
import * as THREE from "three";
const canvas = document.querySelector("#canvas");
// Scene
const scene = new THREE.Scene();
// Camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 5);
// Renderer
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const light = new THREE.DirectionalLight(0xffffff, 5);
light.position.set(5, 5, 5);
scene.add(light);
// Geometry and material
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x44aa88,
emissive: 0x112244,
});
// Create mesh
const cube = new THREE.Mesh(boxGeometry, material);
scene.add(cube);
// Apply transformations
cube.position.set(2, 1, 0); // Position: right and up
cube.scale.set(1.5, 1, 0.5); // Scale: wider and thinner
cube.rotation.x = Math.PI / 4; // Initial rotation
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Animate rotation
cube.rotation.x += 0.01;
cube.rotation.y += 0.005;
renderer.render(scene, camera);
}
animate();
// Handle resize
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Local Space vs World Space
One of the most important concepts in 3D graphics is understanding the difference between local and world space.
Local Space: Coordinates relative to the object itself
World Space: Coordinates relative to the entire scene
The Concept
Imagine you're sitting in a moving car. If you move your hand 1 foot to the right (in the car), that movement is in local space - relative to the car. But the car itself is moving down the road in world space. Your hand's actual position in the world is the combination of both movements.
Parent-Child Relationships
When you add an object as a child of another object, the child's transformations become relative to its parent.
// Parent cube (red)
const parent = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
scene.add(parent);
// Child cube (blue)
const child = new THREE.Mesh(
new THREE.BoxGeometry(0.5, 0.5, 0.5),
new THREE.MeshStandardMaterial({ color: 0x0000ff })
);
// Add child to parent (not to scene!)
parent.add(child);
// Child position is relative to parent
child.position.x = 2; // 2 units to the right of parent
// Move parent in world space
parent.position.x = -2; // 2 units to the left in world
// Animate
function animate() {
requestAnimationFrame(animate);
// Rotate parent - child rotates with it!
parent.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
What's Happening
The blue cube is 2 units to the right of the red cube (local space)
The red cube is at position (-2, 0, 0) in world space
The blue cube's world position is automatically calculated as (0, 0, 0)
When the parent rotates, the child rotates around the parent's center
Getting World Position
Sometimes you need to know an object's actual position in world space:
const worldPosition = new THREE.Vector3();
child.getWorldPosition(worldPosition);
console.log(worldPosition); // Actual position in world space
This is useful for collision detection, distance calculations, or positioning effects relative to world coordinates.
Groups: Organizing Multiple Objects
Groups allow you to treat multiple objects as a single unit. This is essential for building complex scenes with logical hierarchies.
Why Use Groups?
Organize related objects (like parts of a character or vehicle)
Transform multiple objects together
Create hierarchical structures (solar systems, robots, etc.)
Simplify scene management
Creating and Using Groups
// Create a group
const group = new THREE.Group();
scene.add(group);
// Create first cube (green)
const cube1 = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x00ff00 })
);
cube1.position.x = -2; // Position relative to group
group.add(cube1);
// Create second cube (blue)
const cube2 = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x0000ff })
);
cube2.position.x = 2; // Position relative to group
group.add(cube2);
// Transform the entire group
group.position.z = -3; // Move both cubes back
group.rotation.y = Math.PI / 4; // Rotate both cubes
// Animate
function animate() {
requestAnimationFrame(animate);
// Rotating the group rotates both cubes around the group's center
group.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
Hierarchical Transformation
The power of groups comes from hierarchical transformations:
Child transforms happen first - Each cube maintains its local position
Parent transforms apply afterward - The group's rotation affects both cubes
The result is combined - Both cubes rotate around the group's center while maintaining their spacing
This is how complex objects like characters, vehicles, and mechanical systems are built. For example, a car might have:
Car body (group)
Wheels (children of body)
- Hubcaps (children of wheels)
When the car moves, everything moves. When a wheel rotates, only that wheel and its hubcap rotate.
When to Use Each Technique
Direct Transformations (position, rotation, scale):
Simple animations
Independent objects
Single mesh manipulation
Parent-Child Relationships:
Objects that move together
One object attached to another
Hierarchical structures
Groups:
Multiple related objects
Complex scenes with many parts
Logical organization of scene elements
What I Learned
1. Transformations Are Always Relative
Position, rotation, and scale are relative to an object's parent (or the scene if it has no parent). This makes hierarchical structures powerful but requires understanding the coordinate system.
2. Transform Order Matters
Three.js applies transforms in a fixed order: scale → rotation → position. You can't change this, so plan your transformations accordingly.
3. Radians, Not Degrees
Rotation uses radians. Remember: Math.PI is 180°, Math.PI * 2 is 360°. Converting from degrees: degrees * (Math.PI / 180).
4. Local vs World Space Is Fundamental
Understanding the difference between local and world space is crucial for complex scenes. Use getWorldPosition() when you need absolute coordinates.
5. Groups Simplify Complex Scenes
Instead of manually calculating relative positions for multiple objects, use groups to create logical hierarchies. This makes your code cleaner and more maintainable.
6. Accumulating Transforms Creates Animation
Using += on rotation or position values in an animation loop creates continuous movement. Using = sets absolute values.
7. Non-Uniform Scaling Is Powerful
Scaling different amounts on different axes creates interesting effects. A sphere scaled as (1, 2, 1) becomes an egg shape.
Wrapping Up
Transformations are the building blocks of all 3D movement and animation. Mastering position, rotation, and scale - along with understanding local vs world space and hierarchical transformations - gives you complete control over your 3D scenes.
Start with simple transformations on individual objects, then experiment with parent-child relationships and groups. Try building a simple solar system (sun with orbiting planets) or a simple character (body with rotating arms) to practice these concepts.
The key is understanding how transforms combine and propagate through hierarchies. Once this clicks, you can build complex, animated 3D structures with ease.
Keep experimenting!



