Course outline
Coordinates and Axes
In one line
Every position in a 3D scene is three numbers, x, y and z, measured from one point called the origin. three.js uses a right-handed system with y pointing up, and it will happily accept numbers that are negative, enormous or not numbers at all.
Think of it like this
In a tall building you can say where a person is with three numbers: how far along the corridor, which floor, and how far from the window. 3D positions work the same way. Three numbers, each one measured from a fixed starting point, say exactly where something is.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- coordinate
- A number that says how far a point is along one direction, measured from the origin.
- axis
- One of the three directions, x, y or z, that positions are measured along.
- origin
- The point (0, 0, 0) where all three axes meet; every position is measured from it.
- right-handed
- A choice of directions where +x is right, +y is up and +z points towards you. three.js and OpenGL use it.
Step by step
01Draw the axes
new THREE.AxesHelper(2) draws three lines of length 2 from the origin: red along x, green along y and blue along z. Colour order is easy to remember: RGB = XYZ. Add one to any scene you are debugging and you can always tell which way is which.
02Put a point in the scene
dot.position.set(1, 1.5, −2) moves a small ball to that place. The numbers are always in the order x, y, z. In the first example below, four balls sit at points you can read straight from their names: A on the x axis, B on the y axis, C on the z axis, and D somewhere in between.
03Check the hand rule
The last line asks three.js for the cross product of x and y (Topic 2.3 explains it properly). It prints (0, 0, 1): the result points along +z, exactly what the right-hand rule says. The maths and your right hand agree.
04Try the strange numbers
The second example puts a ball at the origin, far in front, behind the camera, absurdly far to the side, and at NaN. For each one it renders and prints how many draw calls three.js made. Look at the numbers carefully: they are not what most people expect.
Try it yourself
- 1
Move a point
In the first example, change
A: [1, 0, 0]toA: [2, 0, 0]. The console printsA = (2, 0, 0)and the ball on the red line moves twice as far from the origin. - 2
Bring the camera back
In the second example, change
camera.position.set(0, 1, 5)tocamera.position.set(0, 1, 50). Now the ball at z = 20 is in front of the camera, so its line printsdraw calls 1. The ball at z = −30 is 80 units away, still inside the far limit of 100. The 1e9 ball is still skipped andNaNstill counts as a draw call.
Code & diagrams
Red is x, green is y, blue is z. Orange balls are the four points. Open the scene tree to see the four meshes named A to D.
Console
Run the scene to see its console.Expected console output
A = (1, 0, 0)
B = (0, 1.5, 0)
C = (0, 0, 2)
D = (1, 1, -2)
x cross y = (0, 0, 1)One ball is moved through five positions. After each render the console prints how many draw calls three.js sent. The picture shows the last state: two balls near the origin.
Console
Run the scene to see its console.Expected console output
at the origin: draw calls 1
z = -30 (far away): draw calls 1
z = 20 (behind the camera): draw calls 0
x = 1e9 (huge): draw calls 0
x = NaN: draw calls 1Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the z number
In the first example, change dot.position.set(x, y, z); to dot.position.set(x, y);.
Myth vs fact
Myth
In 3D, up is always the z axis.
Fact
It depends on the tool. three.js, Maya and glTF files use y up. Blender and many CAD tools use z up. When a model looks lying on its side, this is the first thing to check.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
three.js stores positions as 64-bit JavaScript numbers but sends vertices to the GPU as 32-bit floats. A 32-bit float has about seven digits of precision, so far from the origin tiny steps disappear ("jitter"). Large worlds move the camera and the world together so numbers stay small.
- ▸
The frustum culling test uses each object's bounding sphere. With a
NaNposition every comparison in the test is false, so nothing says "outside" and the object is not culled: the output above showsdraw calls 1for NaN. The GPU then has no valid triangle to draw. Culling is a CPU decision; correctness of the numbers is not checked.
Remember this
- 1
The problem this solves: to put anything in a scene you must say where it goes, and to read someone else's code you must know what its numbers mean. If you guess the directions, tubes end up behind the camera or under the floor. A few minutes on the axes saves hours of guessing.
- 2
Axes and the origin. A coordinate is a number that says how far you are along one direction. The three directions are the axes: x, y and z. The point where all three numbers are 0 is the origin, written (0, 0, 0). A position such as (1, 1.5, −2) means: 1 along x, 1.5 along y, 2 backwards along z.
- 3
Which way is which. In three.js, +x points to the right of the screen, +y points up and +z points towards you, out of the screen. This is a right-handed system: point the fingers of your right hand along +x, curl them towards +y, and your thumb points along +z. Some tools (Unity, DirectX) use a left-handed system, so numbers copied from them may be mirrored.
- 4
Units. three.js has no built-in unit. One unit is whatever you decide. This course decides one unit is one metre, so the Lumen bike's wheel radius of 0.33 means 33 centimetres, and the display stand has radius 1.
- 5
Not a number.
NaN("not a number") can sneak into a position when a calculation divides zero by zero or reads a missing value. three.js does not complain. It accepts the position, may even send the object to the GPU, and the object is invisible. When something vanishes without an error, log its position first.
Explain it without notes
Explain the right-handed coordinate system three.js uses, and what changes if a model comes from a left-handed tool.
A mesh vanishes after you set its position from a calculation. List what you check, in order.
Practice
Write code that puts a small ball at each corner of a square on the floor, 2 units on a side, centred on the origin. Print each corner.
Write a helper isUsable(position) that returns false for NaN, infinite or absurdly large (over 1e6) values.
Done when you can
Say which way +x, +y and +z point in three.js, and use the right-hand rule
Read a position such as (1, 1.5, −2) and say where it is
Add an AxesHelper to debug which way is which
Explain why a NaN position is invisible but is not removed by culling