Course outline
The Scene Graph
In one line
Objects form a tree: a parent carries its children. Move, turn or scale the bike and the wheels follow, while each wheel still spins on its own. You learn how local and world positions relate and how to print the tree.
Think of it like this
A person on a train. The person walks down the carriage (their own movement) while the carriage moves with the train (the parent's movement). To someone on the platform the two add together, and nobody has to calculate the sum.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- scene graph
- The tree of objects in a scene. Each object's position, rotation and scale are measured from its parent.
- local transform
- An object's position, rotation and scale relative to its parent.
- world transform
- Its position, rotation and scale in the scene's own space, after all its ancestors' transforms are applied. Stored in
matrixWorld. - pivot
- The point an object turns around. In three.js it is the object's own origin, so you move a pivot by wrapping the object in a group.
Step by step
01Make a group for the whole bike
const bike = new THREE.Group(); bike.name = "bike"; scene.add(bike);. The group draws nothing; it is a handle for the whole bike.
02Make a group for each part that spins
createWheel returns a group placed at the axle: wheel.position.set(x, 0.35, 0). The torus and the spoke inside sit at the group's origin, so wheel.rotation.z spins them around the axle.
03Add the parts to the bike
bike.add(rearWheel, frontWheel). Their positions are now measured from the bike's origin, which is the middle of the stand.
04Move the parent and read the world
bike.position.x = 1, then scene.updateMatrixWorld() and wheel.getWorldPosition(new THREE.Vector3()). The world position changes, the local one does not.
Try it yourself
- 1
Turn the parent
In the first example replace
bike.scale.setScalar(2);withbike.rotation.y = Math.PI / 2;(rename the label to"bike turned 90"). The last line printsworld: 1, 0.35, -0.58: the front wheel swung around the bike's origin, then moved with the bike to x = 1. - 2
Turn the parent and watch the pivot
In the first example add
bike.rotation.y = time / 1500;to the loop. The whole bike, with both wheels, swings around the point (0, 0, 0), the bike group's origin, not around its own middle. A group turns about its own origin; its children go with it.
Code & diagrams
Two levels of movement: the bike's position (parent) and each wheel's rotation (child). The console shows the world position after each parent change.
Console
Run the scene to see its console.Expected console output
start | front wheel local x: 0.58 | world: 0.58, 0.35, 0
bike moved +1 | front wheel local x: 0.58 | world: 1.58, 0.35, 0
bike scaled x2 | front wheel local x: 0.58 | world: 2.16, 0.7, 0Try it: interactive
Move the parent, read the child
Move and turn the frame (parent) with the sliders and watch the wheel's local numbers stay the same while its world numbers change. Then scale the parent.
A small recursive function prints names and types. The two tyres share one geometry and one material: the tree has two tyres, the GPU holds one shape.
Console
Run the scene to see its console.Expected console output
(no name) [Scene]
turntable [Group]
bike [Group]
rear-wheel [Group]
tyre [TorusGeometry]
front-wheel [Group]
tyre [TorusGeometry]
(no name) [AmbientLight]
tyres: 2 | same material: true | same geometry: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Squash a parent that carries a turned child
In the first example delete the line bike.scale.setScalar(1); and change bike.scale.setScalar(2); to bike.scale.set(1, 3, 1);.
Myth vs fact
Myth
position is the object's place in the scene.
Fact
It is its place in its parent. It equals the scene position only when the parent is the scene, or when every ancestor is at the origin, unturned and unscaled.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
render()callsscene.updateMatrixWorld()every frame. If you readgetWorldPositionbetween a change and the nextrender(), callupdateMatrixWorld()yourself, as the examples do, or you get the previous frame's answer. - ▸
For objects that never move, set
object.matrixAutoUpdate = false(and callupdateMatrix()once): the tree walk skips recomputing them. Phase 17 shows when this matters.
Remember this
- 1
The problem this solves: a bike has about twenty parts that move together, and some also move alone (a wheel turns). Setting every part's world position each frame is twenty updates and a lot of maths. With a tree you move the bike and the parts follow.
- 2
The scene graph is the tree of
Object3Ds from theScenedown. Eachposition,rotationandscaleis local, measured in the parent's space. The world position comes from multiplying the parents' matrices down from the scene (Topic 2.5). You only set local numbers. - 3
Move a parent, children follow. The wheels sit at x = −0.55 and 0.58 inside
bike.bike.position.x = 1changes no wheel number, and the front wheel's world x goes from 0.58 to 1.58. Wheels spin withwheel.rotation.zwhile the bike rolls: one movement on each level. - 4
Scale a parent and everything scales, positions included. With
bike.scale.setScalar(2)the front wheel's world position is (2.16, 0.7, 0): its local (0.58, 0.35, 0) doubled, plus the bike's x of 1. A uniform scale is harmless; a non-uniform one on a parent stretches turned children (breakIt). Keep scale on leaf meshes. - 5
Turn a parent and children orbit its origin. A 90° turn about y moves the front wheel from (0.58, 0.35, 0) to about (0, 0.35, −0.58) in the bike's frame, and (1, 0.35, −0.58) with the bike at x = 1. A group turns about its own origin, so to spin a part about its own centre put it in a group at that centre (a wheel group at the axle). The Sun–Earth–Moon demo works the same way.
- 6
Why keep the tree. Hiding a parent hides its subtree (Topic 3.4), disposing of a part walks its subtree, and picking (Phase 8) walks up the
parentchain. Keep it shallow and named: a group per part, and one top group (bike) that tools and tests can find. The scene inspector under each canvas shows this tree.
Explain it without notes
A wheel is a child of the bike. The bike moves 1 m forward. What are the wheel's local and world positions before and after?
Why does a child turn around its parent's origin and not its own centre? How do you make a part spin about its own centre?
Practice
Build a crank group that holds two pedals 0.17 m on each side of its centre and turns once every 2 seconds.
Print how many objects are below bike in a scene where the bike has two wheels with two meshes each.
Done when you can
Explain local versus world position with the bike and its wheels
Move and scale a parent and predict a child's world position
Put a part's pivot at the right point with a group
Read the scene inspector's tree and match it to the code