Skip to main content

Command Palette

Search for a command to run...

Understanding Transformations in Three.js

Updated
9 min read
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:

  1. Child transforms happen first - Each cube maintains its local position

  2. Parent transforms apply afterward - The group's rotation affects both cubes

  3. 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!

ReactJs

Part 1 of 1