Three.js
Three.js is the standard library for 3D graphics in the browser. It wraps WebGL (and increasingly WebGPU) — the low-level GPU API that is powerful but brutally verbose — in an approachable scene-graph model: create a scene, add objects with geometry and materials, point a camera at it, and render. Product configurators, data visualizations, portfolio showpieces, interactive maps, and browser games all run on it.
Raw WebGL requires hundreds of lines of shader code and buffer management to draw a single spinning cube. Three.js makes that cube a dozen lines while keeping the full power accessible when you need it. It's the connective tissue between "I know JavaScript" and "I can put interactive 3D on a web page."
TL;DR
- The core trio: a Scene (holds objects), a Camera (viewpoint), and a Renderer (draws the scene to a
<canvas>via WebGL/WebGPU). - Objects are Meshes = Geometry (shape/vertices) + Material (surface/appearance). Add Lights because most materials need illumination to be visible.
- You drive an animation loop (
requestAnimationFrame) that updates and re-renders each frame — 3D is real-time, not static. - Performance is the whole game: draw calls, polygon counts, texture sizes, and geometry disposal determine whether it runs at 60fps or melts a phone.
- React Three Fiber lets you write Three.js declaratively as React components — the dominant way to use it in React apps.
- 3D is a genuine discipline: coordinates, matrices, lighting models, and the GPU pipeline are real concepts, and shaders (GLSL) are the deep end.
Quick Example
A lit, rotating cube — the "hello world" of WebGL, in Three.js:
Core Concepts
The Scene Graph
Three.js organizes everything as a tree of objects with positions, rotations, and scales — parent transforms cascade to children (rotate a car group, its wheels come along). The renderer walks this graph each frame and draws it from the camera's viewpoint.
Meshes: Geometry + Material
Every visible object is a Mesh combining:
- Geometry — the shape's vertices: built-in primitives (
BoxGeometry,SphereGeometry,PlaneGeometry) or loaded from 3D model files. - Material — how the surface looks and responds to light:
The gotcha every beginner hits: a MeshStandardMaterial scene with no lights renders pure black. PBR materials need illumination.
Loading Real Models and Textures
Primitives get you started; real 3D content comes from artists as glTF (the web-standard 3D format — "the JPEG of 3D"):
Textures (image files mapped onto surfaces), environment maps (for reflections), and skeletal animations all load similarly. OrbitControls adds click-drag camera movement in one line.
The Render Loop and Interaction
3D is real-time: requestAnimationFrame calls your loop each frame (~60fps), where you update object states and re-render. Delta-time (clock.getDelta()) keeps motion frame-rate independent. Clicking objects uses raycasting — projecting a ray from the camera through the cursor to find what it hits.
WebGL, WebGPU, and Shaders
Under Three.js sits WebGL (OpenGL ES in the browser); the emerging WebGPU backend brings modern GPU features and better performance, and Three.js's node-material system targets both. At the bottom are shaders — GLSL programs running on the GPU per-vertex and per-pixel — which power every custom visual effect. You can go far without writing shaders; mastering them is where browser 3D becomes a specialty.
Performance: The Central Discipline
Browser 3D lives or dies on frame rate, and the levers are specific:
- Draw calls: each mesh drawn separately is a GPU call; hundreds tank performance. Merge geometries and use instancing (
InstancedMesh) to draw thousands of identical objects (trees, particles) in one call. - Polygon budget: model detail costs vertices. Use level-of-detail (LOD) — swap high-poly for low-poly at distance.
- Textures: large textures eat GPU memory; compress (KTX2/Basis), size to power-of-two, reuse.
- Dispose geometry and materials you remove — Three.js does not garbage-collect GPU resources; leaked buffers are a real memory bug (
geometry.dispose(),material.dispose()). - Cap pixel ratio on high-DPI screens (rendering at 3× native resolution is 9× the pixels).
- Profile with
renderer.info(draw calls, triangles) and the browser's GPU tools — the same web performance rigor, applied to the GPU.
Mobile is the real test: a scene that flies on a desktop GPU can hit 15fps on a mid-range phone.
React Three Fiber and the Ecosystem
In React apps, imperative Three.js code fights React's model. React Three Fiber (R3F) reconciles them — Three.js objects become JSX components:
Same Three.js underneath, but declarative, composable, and integrated with React state. The drei helper library adds cameras, controls, loaders, and effects as ready components. For most React-based 3D work, R3F is the default entry point. Beyond it: Babylon.js (a heavier, more game-engine-like alternative) and PlayCanvas occupy the fuller-engine end.
Common Mistakes
Black Screen (No Lights)
The rite of passage: a MeshStandardMaterial scene with no light source renders black. Add an AmbientLight and a DirectionalLight, or use MeshBasicMaterial for unlit content.
Not Disposing GPU Resources
Removing a mesh from the scene doesn't free its GPU memory — geometries, materials, and textures must be explicitly .dispose()d. In a long-running app (or one that loads many models), leaked buffers accumulate into crashes.
Ignoring Mobile
Desktop GPUs hide sins. Test on real mid-range phones early; the draw-call, polygon, and texture budgets that feel generous on a laptop are tight on mobile.
Fighting React with Imperative Three.js
Wiring raw Three.js into a React component (manual refs, effects, cleanup) is painful and bug-prone. If you're in React, use React Three Fiber — it exists precisely to make this pleasant.
Skipping the Math
Coordinates, transforms, cameras, and lighting are real concepts; treating Three.js as a black box leads to "why is my object off-screen / inside-out / invisible" flailing. A little vector/matrix intuition saves hours.
FAQ
Do I need to know graphics/math to use Three.js?
Basic 3D concepts (coordinates, cameras, transforms, lighting) — yes, and Three.js is a great way to learn them. Deep linear algebra and shader (GLSL) programming — only for advanced custom visuals. You can build product configurators and data viz with the high-level API and light math.
Three.js or a game engine like Unity?
Three.js targets the web — it ships as part of a web page, integrates with your DOM/React UI, and needs no plugin or download. Unity/Godot target richer games with full editors and physics but export heavier web builds. For web-embedded 3D (configurators, viz, marketing, casual games), Three.js; for a serious 3D game, a game engine.
What's the difference between WebGL and WebGPU here?
WebGL is the mature, universally-supported GPU API Three.js has always used; WebGPU is the modern successor with better performance and compute capabilities, now supported by Three.js's newer backend. You can largely ignore the distinction and let Three.js pick — WebGPU where available, WebGL fallback.
How do I get 3D models into a scene?
Export/obtain them as glTF/GLB (the web standard) and load with GLTFLoader. Artists produce these in Blender/Maya; marketplaces and libraries (Sketchfab, Poly Haven) provide ready assets. glTF carries geometry, PBR materials, and animations together.
Is it hard to get good performance?
It requires attention, not genius. The rules are known and specific (minimize draw calls, budget polygons and textures, dispose resources, cap pixel ratio, instance repeated objects) — apply them and 60fps is very achievable, including on mobile. Ignoring them is how "3D is slow" myths start.
Related Topics
- React — Paired with Three.js via React Three Fiber
- JavaScript — The language it's written in
- Web Performance — The same rigor, applied to the GPU
- WebAssembly — For compute-heavy 3D workloads
- Canvas & graphics — The
<canvas>element Three.js draws to - PWA — Shipping 3D experiences as installable apps