Exercises
Six small builds that flex the parts of three.js you actually use day-to-day. Each is solvable in 20–30 minutes; the goal is to internalise the boring-but-essential setup that turns a beautiful demo into a stable scene.
Six three.js drills with hints
EXAMPLE
import * as THREE from 'three';
// ============================================================
// Drill 1 — Correct colour space + tonemapping
// ============================================================
// TASK: set up a renderer so JPG/PNG textures look correct (not washed out).
// HINTS:
// - renderer.outputColorSpace = THREE.SRGBColorSpace
// - renderer.toneMapping = THREE.ACESFilmicToneMapping
// - each loaded texture: tex.colorSpace = THREE.SRGBColorSpace
//
// SOLUTION:
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// ============================================================
// Drill 2 — Resize handler that keeps aspect ratio correct
// ============================================================
function onResize(camera, renderer) {
renderer.setSize(innerWidth, innerHeight);
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
}
addEventListener('resize', () => onResize(camera, renderer));
// Common bug: forgetting updateProjectionMatrix() -> objects stretch on resize.
// ============================================================
// Drill 3 — Animate without setTimeout drift
// ============================================================
const clock = new THREE.Clock();
function frame() {
const dt = clock.getDelta(); // seconds since last frame
mesh.rotation.y += dt * 0.5; // dt-driven rotation = framerate independent
renderer.render(scene, camera);
}
renderer.setAnimationLoop(frame);
// ============================================================
// Drill 4 — Load a GLB and play its first animation
// ============================================================
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
let mixer;
loader.load('/model.glb', (gltf) => {
scene.add(gltf.scene);
mixer = new THREE.AnimationMixer(gltf.scene);
mixer.clipAction(gltf.animations[0]).play();
});
function tick(dt) { mixer?.update(dt); }
// Common bug: forgetting mixer.update -> animation never plays.
// ============================================================
// Drill 5 — Raycast clicks to highlight objects
// ============================================================
const ray = new THREE.Raycaster();
const pointer = new THREE.Vector2();
let highlighted;
addEventListener('pointerdown', (e) => {
pointer.set((e.clientX/innerWidth)*2-1, -(e.clientY/innerHeight)*2+1);
ray.setFromCamera(pointer, camera);
const hit = ray.intersectObjects(scene.children, true)[0]?.object;
if (hit !== highlighted) {
highlighted?.material?.emissive?.setHex(0x000000);
hit?.material?.emissive?.setHex(0x224488);
highlighted = hit;
}
});
// ============================================================
// Drill 6 — Dispose properly when a scene changes
// ============================================================
function dispose(obj) {
obj.traverse((node) => {
if (node.geometry) node.geometry.dispose();
if (Array.isArray(node.material)) node.material.forEach((m) => m.dispose());
else if (node.material) node.material.dispose();
});
scene.remove(obj);
}
// Without this, switching scenes leaks GPU memory until the browser kills the tab.
// ============================================================
// Scoring
// ============================================================
// 6 / 6 -> ready to ship to production
// 4 / 6 -> revisit the colour-management + lifecycle docs
// < 4 -> three.js fundamentals course on three-fundamentals.org
Why it matters
Three boilerplate fixes — correct colour space, dt-driven animation, and dispose() on scene change — turn 90% of \"my three.js demo eats my MacBook\" complaints into stable scenes. They are unglamorous and they ship with every production build.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…