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 InstancedMesh for repeated objects with different transforms.
  • Mutate buffer attributes in place and set needsUpdate only 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