Three.js Documentation
A practical and SEO-focused Three.js guide for building fast, maintainable WebGL scenes in production. This page covers rendering architecture, cameras, geometry, materials, shadows, performance tuning, and advanced post-processing workflows.
Deep Dive Guides
Quick Start: Scene, Camera, Renderer
Every Three.js app starts with three core pieces: a Scene (what exists), a Camera (how you view it), and a Renderer (how frames are drawn). Most app-level architecture is built around this lifecycle.
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.z = 3
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)
In production projects, also handle resize events, keep object creation separate from the render loop, and avoid per-frame allocations in hot update paths.
Core Architecture: Object3D Hierarchy
Most renderable objects in Three.js extend Object3D. This gives you consistent transforms (position, rotation, scale) and parent-child graph behavior across meshes, lights, groups, and cameras.
const group = new THREE.Group()
const meshA = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x4a9eff })
)
const meshB = new THREE.Mesh(
new THREE.SphereGeometry(0.4, 24, 24),
new THREE.MeshStandardMaterial({ color: 0xff8855 })
)
meshB.position.x = 1.5
group.add(meshA, meshB)
scene.add(group)
Because transforms are hierarchical, moving the parent group moves both meshes. This pattern is the foundation for composite models, camera rigs, and scene organization.
Geometry and Buffer Attributes
For performance-sensitive scenes, prefer BufferGeometry. It stores vertex data in typed arrays and maps directly to GPU buffers.
const geometry = new THREE.BufferGeometry()
const positions = new Float32Array([
-1, 0, 0,
1, 0, 0,
0, 1, 0
])
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
geometry.computeVertexNormals()
- Use indexed geometry when possible to reduce duplicated vertices.
- Use
InstancedMeshfor repeated objects with different transforms. - Mutate buffer attributes in place and set
needsUpdateonly when required.
Materials and Color Management
Choosing the right material is critical for realism and performance:
MeshBasicMaterial: unlit and very fast.MeshLambertMaterial: diffuse lighting at low cost.MeshPhongMaterial: classic specular highlights.MeshStandardMaterial: physically based rendering (PBR).
renderer.outputColorSpace = THREE.SRGBColorSpace
const material = new THREE.MeshStandardMaterial({
color: 0x9bdc6e,
metalness: 0.2,
roughness: 0.6
})
For modern web projects, MeshStandardMaterial is usually the best default because it scales well from basic lighting to environment-based reflections.
Lighting and Shadows
A practical baseline is one ambient fill light plus one directional key light. This gives depth, readable shading, and reasonable performance.
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
const ambient = new THREE.AmbientLight(0xffffff, 0.25)
scene.add(ambient)
const sun = new THREE.DirectionalLight(0xffffff, 1.2)
sun.position.set(5, 10, 4)
sun.castShadow = true
scene.add(sun)
mesh.castShadow = true
ground.receiveShadow = true
Shadows are one of the most expensive features in real-time rendering. Keep shadow maps as small as acceptable and enable cast/receive shadows only where needed.
Camera and Controls
PerspectiveCamera is the standard camera for 3D scenes. Pair it with OrbitControls for exploratory interfaces or custom movement logic for game-style controls.
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 200)
camera.position.set(4, 3, 6)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.05
controls.target.set(0, 1, 0)
Keep near/far clipping planes tight whenever possible to improve depth precision and reduce z-fighting.
Animation Loop and Timing
Use a single loop and update simulation state with delta time from THREE.Clock for frame-rate independence.
const clock = new THREE.Clock()
function animate () {
requestAnimationFrame(animate)
const dt = clock.getDelta()
// update world using dt
controls.update()
renderer.render(scene, camera)
}
animate()
Textures and Asset Loading
Load textures and models asynchronously, then create meshes once data is ready. Reuse assets and dispose when no longer needed.
const textureLoader = new THREE.TextureLoader()
const albedo = textureLoader.load('/textures/rock.jpg')
albedo.colorSpace = THREE.SRGBColorSpace
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ map: albedo })
)
- Prefer compressed textures for large assets when available.
- Reuse texture and material instances across many meshes.
- Dispose explicitly:
geometry.dispose(),material.dispose(),texture.dispose().
Post-Processing Pipeline
Use EffectComposer when you need screen-space effects like bloom, depth of field, antialiasing, or color grading.
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
const composer = new EffectComposer(renderer)
composer.addPass(new RenderPass(scene, camera))
function animate () {
requestAnimationFrame(animate)
composer.render()
}
Performance Checklist
- Cap pixel ratio:
Math.min(window.devicePixelRatio, 2). - Minimize draw calls with instancing and batching strategies.
- Use shadows selectively and reduce shadow-map resolution where acceptable.
- Reuse geometries/materials and dispose GPU resources when removed.
- Avoid creating new objects inside high-frequency render loops.
- Profile with real devices, not only high-end desktop hardware.
Common Issues and Fixes
- Black screen: verify camera position/target and near/far planes.
- Dark scene: add ambient fill light and confirm material type matches lighting setup.
- Z-fighting: tighten near/far planes and avoid overlapping coplanar meshes.
- Aliasing: enable antialiasing or post-process AA for thin lines.
- Memory leaks: dispose geometry/material/texture objects on cleanup.
FAQ
What should I learn first in Three.js?
Learn scene/camera/renderer first, then meshes, materials, lights, and controls. After that, focus on performance and post-processing.
How do I keep interactive pages SEO friendly?
Publish crawlable HTML content around the canvas, include headings and descriptive text, use canonical URLs, add schema markup, and build internal links across related demos and tutorials.
Can I use Three.js for production websites?
Yes. Keep assets optimized, test mobile performance, use progressive enhancement, and ensure text/content layers remain accessible outside the canvas.
Related Demos and Guides
- Three.js Timer vs Clock Delta Demo (r183)
- Bezier Interpolant Curves (r183-inspired)
- 3D Point Cloud Visualization in Three.js
- Three.js Orbit Controls Playground Demo
- Advanced Anaglyph Stereo Techniques (r183)
- Stereo Rig Sandbox (r183 roadmap)
- Fog Scattering Playground (r183-inspired)
- Volumetric Spotlight Lab (r183 roadmap)
- Water Realism Upgrade (r183-inspired)
- Env IBL on Lambert vs Phong (r183)
- Clearcoat + Anisotropy Lab (r183 roadmap)
- Iridescence Thin-Film Materials (r183 roadmap)
- BatchedMesh Opacity + Wireframe (r183)
- Instancing vs BatchedMesh Benchmark (r183 roadmap)
- Reversed Depth + PostFX Compatibility (r183)
- TRAA vs SMAA Comparison (r183 roadmap)
- Shadow Map Types Comparison (r183 roadmap)
- MRT G-Buffer Visualizer (r183 roadmap)
- Curve Modifier Playground (r183 roadmap)
- Retro Pass Pipeline (r183-inspired)
- Three.js Day-Night Cycle Simulation
- 3D Point Cloud Step-by-Step Tutorial
- All Three.js Tutorials
- Official Three.js Documentation