« Previous
Next »
Summary
A one-screen summary of three.js for app developers: renderer setup, scene + camera + light, geometry + materials, the animation loop, the lifecycle gotchas (resize, dispose), and the helper libraries that get you out of "infinite cube demos" territory.
three.js in one screen
EXAMPLE
// ===== 1) Renderer (boring but essential) =====
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// ===== 2) Scene + camera =====
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111317);
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 200);
camera.position.set(3, 2, 5);
// ===== 3) Lights — PBR materials need them =====
scene.add(new THREE.AmbientLight(0xffffff, 0.3));
const key = new THREE.DirectionalLight(0xffffff, 1.5);
key.position.set(5, 10, 5); key.castShadow = true;
scene.add(key);
// ===== 4) Geometry + material =====
const mesh = new THREE.Mesh(
new THREE.IcosahedronGeometry(1, 4),
new THREE.MeshStandardMaterial({ color: 0x224488, roughness: 0.4, metalness: 0.1 }),
);
mesh.castShadow = mesh.receiveShadow = true;
scene.add(mesh);
// ===== 5) Animation loop =====
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
const dt = clock.getDelta(); // seconds since last frame
mesh.rotation.y += dt * 0.5; // dt-driven = framerate-independent
renderer.render(scene, camera);
});
// ===== 6) Resize handler =====
addEventListener('resize', () => {
renderer.setSize(innerWidth, innerHeight);
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
});
// ===== 7) Asset loading — GLB is the modern format =====
// import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// new GLTFLoader().load('/model.glb', (g) => scene.add(g.scene));
// ===== 8) Controls =====
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// const controls = new OrbitControls(camera, renderer.domElement);
// controls.enableDamping = true; // call controls.update() in the loop
// ===== 9) Raycast click-to-pick =====
const ray = new THREE.Raycaster();
const pointer = new THREE.Vector2();
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) console.log('clicked:', hit.name);
});
// ===== 10) Dispose properly =====
function disposeObject(obj: THREE.Object3D) {
obj.traverse((n: any) => {
if (n.geometry) n.geometry.dispose();
if (Array.isArray(n.material)) n.material.forEach((m: any) => m.dispose());
else if (n.material) n.material.dispose();
});
}
// On unload / scene swap: disposeObject(mesh); renderer.dispose(); renderer.setAnimationLoop(null);
// ===== 11) Libraries that get you past the demo stage =====
// three-mesh-bvh -> fast raycasting on huge geometries
// react-three-fiber -> React renderer for three.js
// drei -> ready-made helpers (Loaders, controls, post-processing)
// physics -> rapier-3d, cannon-es
// gsap -> tweening / camera fly-throughs
// EffectComposer -> bloom, vignette, FXAA (see threejs/postprocess lesson)
// ===== 12) Decision matrix =====
// - Static product visualiser? plain three.js + GLB + OrbitControls
// - Interactive game-ish? add gsap + physics + Howler audio
// - Game? maybe Babylon.js or PlayCanvas instead
// - Inside a React app? react-three-fiber + drei
// - 100s of moving objects? InstancedMesh + GPU compute
// ===== 13) Performance tips =====
// - Cap pixel ratio at 2; mobile devices die at 3
// - Use InstancedMesh for repeated geometry
// - Bake lighting where possible; dynamic shadows are expensive
// - Stream + dispose; never keep all asset memory live
// - Test on a mid-tier Android, not a flagship MacBook
Why it matters
Three lines at startup — outputColorSpace = SRGB, toneMapping = ACES, dispose on unload — separate a hobby demo from a viewer you can ship. Add OrbitControls + GLB loading + a resize handler and you have the entire skeleton a production 3D viewer needs.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Next: react-three-fiber, post-processing, custom shaders, performance tuning.Try it Yourself »
« Previous
Next »
Discussion
Loading…