Course outline
Your First Scene on This Page
In one line
Fifteen lines make a turning cube: a scene, a camera, a renderer, one mesh and a loop. You change its colour, size and speed, learn what every line does, and learn the five-step checklist for a black screen.
Think of it like this
A puppet show needs a stage, the puppets, a lamp and someone in the audience looking at it. In three.js the scene is the stage, a mesh is a puppet, a light is the lamp, and the camera is the person watching. The renderer is the photographer who takes a picture of what that person sees, 60 times a second.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- mesh
- A visible object in three.js: a geometry (the shape) plus a material (how its surface looks).
- geometry
- The shape of a mesh, stored as corner points (vertices) joined into triangles.
BoxGeometry(1, 1, 1)is a cube 1 unit on each side. - material
- How a mesh's surface looks: its colour, how shiny it is, whether light affects it.
- camera
- The viewpoint the scene is drawn from. A
PerspectiveCameramakes far things look smaller, like your eyes do. - draw call
- One drawing job sent to the GPU, usually one mesh. Fewer draw calls means less work per frame.
- animation loop
- A function the browser runs before every screen refresh (about 60 times a second).
renderer.setAnimationLoop(fn)sets one up for you.
Step by step
01Import three.js
import * as THREE from "three"; puts the whole library under one name, THREE. Every class you use starts with it: THREE.Scene, THREE.Mesh. On this page the import is answered by the course's copy of three.js r186; on your computer it comes from the package you install in Topic 0.4.
02A scene and a camera
new THREE.Scene() makes an empty stage. new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 100) makes a camera with a 50° view from top to bottom, the same shape as the window, that sees things between 0.1 and 100 units away.
Every object starts at the centre point, (0, 0, 0). The cube will be there too, so camera.position.z = 3 steps the camera 3 units back towards you, out of the cube.
03A renderer and its canvas
new THREE.WebGLRenderer({ antialias: true }) creates a canvas and a WebGL 2 connection to the GPU; antialias smooths jagged edges. setSize(innerWidth, innerHeight) makes the canvas fill the window, and document.body.append(renderer.domElement) puts it on the page. Without that last line the drawing happens, but nobody can see it.
04A mesh: shape plus look
new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial()) joins a 1 × 1 × 1 box shape with a material that colours each face by the direction it faces. That material needs no light, so it is a safe choice for a first test. scene.add(cube) puts it on the stage.
05The loop
renderer.setAnimationLoop((time) => { ... }) runs the function before every screen refresh and passes the time in milliseconds since the page started. Inside, the cube is turned a little (time / 1000 radians, so it turns about once every 6.3 seconds around y) and renderer.render(scene, camera) draws the new frame.
The last line prints how many objects the scene holds: one, the cube. The camera is not in the scene; it doesn't need to be.
Try it yourself
- 1
Turn the three knobs
In "Three knobs", set
COLORto"hotpink",SIZEto2andSPEEDto0. The console printscube colour: #ff69b4(three.js turns the CSS colour name into its hex value) andcube size: 2 × 2 × 2, and the cube stops turning. A size-2 cube nearly fills the view, because the camera is only 3.5 units away. - 2
Fix the checklist scene
In the checklist example, fix the three mistakes one at a time and press Run after each: set
camera.position.set(0, 0, 3), addscene.add(new THREE.AmbientLight("#ffffff", 1));, and addrenderer.render(scene, camera);just beforeblackScreenChecklist();. When all three are fixed, every line saysokand a flat, dark orange square appears on a black background: an ambient light shines equally from all directions, so the cube's front face has no shading and looks like a square. Real lighting with a direction comes in Phase 5. - 3
Watch the status bar
In the first scene, open the scene tree and watch the status bar while the cube turns: 1 draw call, 12 triangles, a steady frame rate. Then press Pause: the canvas stops and the GPU is free. Press Run to start again.
Code & diagrams
Every line is explained in the steps above. The output comes from the last line.
Console
Run the scene to see its console.Expected console output
objects in the scene: 1Change the three constants at the top. MeshStandardMaterial reacts to light, so this scene has two lights.
Console
Run the scene to see its console.Expected console output
cube colour: #ff6b2c
cube size: 1 × 1 × 1This scene shows nothing on purpose. The checklist finds all three planted mistakes.
Console
Run the scene to see its console.Expected console output
1. canvas on the page with a size: ok
2. render() has been called: PROBLEM: nothing was ever drawn
3. camera outside the object: PROBLEM: the camera is inside the cube
4. light for this material: PROBLEM: MeshStandardMaterial needs a light
5. errors in the console: look above this listBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
A typo in the import
In the first scene, change from "three" to from "thre".
Break #2
Forget the word new
In the first scene, change new THREE.BoxGeometry(1, 1, 1) to THREE.BoxGeometry(1, 1, 1).
Break #3
Render without a camera
In the first scene, change renderer.render(scene, camera); to renderer.render(scene);.
Break #4
Forget to render
In the first scene, delete the line renderer.render(scene, camera); inside the loop (keep the loop's other two lines).
Myth vs fact
Myth
If nothing appears, three.js must have failed to load.
Fact
Most of the time three.js works perfectly and draws exactly what you asked for: from inside the cube, with no light, or never. Run the five-step checklist before suspecting the library.
Myth
The camera has to be added to the scene.
Fact
Not for drawing: render(scene, camera) takes the camera separately. You only add a camera to the scene when something is attached to it, such as a light that moves with it.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
setAnimationLoopis three.js's wrapper aroundrequestAnimationFrame. It is required for WebXR (Phase 18), because a headset sets the frame rate, not the page, and the WebGPU renderer (Phase 13) uses it to wait until the GPU is ready before the first frame. The browser pausesrequestAnimationFramein background tabs, so a hidden page costs almost no GPU time. - ▸
renderer.info.renderis reset at the start of everyrender()call by default (info.autoReset). If you render several times per frame (for example with post-processing in Phase 15), the numbers only describe the last call unless you reset them yourself. - ▸
The checklist's step 3 uses
Box3.setFromObject, which builds an axis-aligned bounding box around an object in world space. A camera *inside* a closed box sees nothing because the box's faces point outwards, and three.js skips faces seen from behind (back-face culling).
Remember this
- 1
The problem this solves: most first attempts at 3D end with a black rectangle and no error message. If you know the five things every scene needs, and the five checks for a black screen, you can always find out why.
- 2
The five things. A
Scene(holds everything), aPerspectiveCamera(where you look from: field of view in degrees, the screen's width÷height, and the nearest and farthest distance it can see), aWebGLRenderer(draws into a canvas you add to the page), aMesh(a shape, its geometry, plus its look, its material) and a loop that callsrenderer.render(scene, camera)again and again. - 3
Where you write code. In the editor of each live example on this page. It changes the scene about 0.6 seconds after you stop typing, or straight away when you press Run (or Ctrl+Enter). If your system is set to "reduce motion", scenes do not start by themselves: press Run. Your edits are saved in this browser; the reset button brings back the original. Pause stops the scene and frees the GPU; scenes also stop by themselves when you scroll far away from them.
- 4
Reading the status bar. Under each canvas you see the backend (
WebGL 2,WebGPUorWebGL 2 (fallback)), the draw calls (how many separate drawing jobs were sent to the GPU in the last frame), the triangles, the geometries and textures in GPU memory, the shaders, the pixel ratio and the frames per second. These numbers are how professionals see what a scene costs; you will use them from day one. - 5
The black-screen checklist. 1. Is the canvas on the page, with a size? 2. Is
render()being called? 3. Is the camera outside the object, and looking at it? 4. Does the material need light, and is there any? 5. What does the console say? The third live example runs this checklist as code against a scene with three planted mistakes.
Explain it without notes
Name the five things every three.js scene needs, and what each one does, using the puppet-show picture.
A friend's scene shows a black screen and no error. Walk them through the five checks.
Practice
Change the first scene so the cube turns only around its y axis, at one full turn every 4 seconds. One full turn is 2π radians.
Change the first scene to show a sphere instead of a cube, without changing its material. Predict the triangle count, then check the status bar.
Done when you can
Write a three.js scene with a scene, camera, renderer, mesh and loop from memory
Change a mesh's colour, size and speed and predict what you will see
Read the status bar: backend, draw calls, triangles
Run the five-step black-screen checklist on a broken scene
Read three real error messages and say which line caused each