Command Palette

Search for a command to run...

Hectal
Course outline
Topic 0.3Beginner ~23 min · step 3 of 6

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.

0/6 · 0%

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 PerspectiveCamera makes 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. 1

    Turn the three knobs

    In "Three knobs", set COLOR to "hotpink", SIZE to 2 and SPEED to 0. The console prints cube colour: #ff69b4 (three.js turns the CSS colour name into its hex value) and cube 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. 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), add scene.add(new THREE.AmbientLight("#ffffff", 1));, and add renderer.render(scene, camera); just before blackScreenChecklist();. When all three are fixed, every line says ok and 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. 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

Your first scene: a turning cube New tab

Every line is explained in the steps above. The output comes from the last line.

idle

Console

Run the scene to see its console.

Expected console output

objects in the scene: 1
Three knobs: colour, size, speed New tab

Change the three constants at the top. MeshStandardMaterial reacts to light, so this scene has two lights.

idle

Console

Run the scene to see its console.

Expected console output

cube colour: #ff6b2c
cube size: 1 × 1 × 1
The black-screen checklist, as code New tab

This scene shows nothing on purpose. The checklist finds all three planted mistakes.

idle

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 list

Break 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".

terminal
$ Run
── what you'll see ──
Uncaught TypeError: Failed to resolve module specifier "thre". Relative references must start with either "/", "./", or "../".

Break #2

Forget the word new

In the first scene, change new THREE.BoxGeometry(1, 1, 1) to THREE.BoxGeometry(1, 1, 1).

terminal
$ Run
── what you'll see ──
Uncaught TypeError: Class constructor BoxGeometry cannot be invoked without 'new'

Break #3

Render without a camera

In the first scene, change renderer.render(scene, camera); to renderer.render(scene);.

terminal
$ Run
── what you'll see ──
objects in the scene: 1
Uncaught TypeError: Cannot read properties of undefined (reading 'parent')
The last line repeats on every frame, because the loop calls the broken line again and again. The `console.log` at the bottom of the file runs first, so it prints before the error.

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).

terminal
$ Run
── what you'll see ──
objects in the scene: 1
No error. The canvas stays empty: you see whatever is behind it, here the preview's dark background. On a page with a white background it would be a white rectangle.

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.

  • ▸

    setAnimationLoop is three.js's wrapper around requestAnimationFrame. 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 pauses requestAnimationFrame in background tabs, so a hidden page costs almost no GPU time.

  • ▸

    renderer.info.render is reset at the start of every render() 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. 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. 2

    The five things. A Scene (holds everything), a PerspectiveCamera (where you look from: field of view in degrees, the screen's width÷height, and the nearest and farthest distance it can see), a WebGLRenderer (draws into a canvas you add to the page), a Mesh (a shape, its geometry, plus its look, its material) and a loop that calls renderer.render(scene, camera) again and again.

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

    Reading the status bar. Under each canvas you see the backend (WebGL 2, WebGPU or WebGL 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. 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

01

Name the five things every three.js scene needs, and what each one does, using the puppet-show picture.

02

A friend's scene shows a black screen and no error. Walk them through the five checks.

Practice

01

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.

02

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