Quick Reference
Standard Initialization Pattern
import {
WebGLRenderer, SRGBColorSpace, ACESFilmicToneMapping,
PCFSoftShadowMap, PerspectiveCamera, Scene
} from 'three';
const renderer = new WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.outputColorSpace = SRGBColorSpace; renderer.toneMapping = ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.shadowMap.enabled = true; renderer.shadowMap.type = PCFSoftShadowMap; document.body.appendChild(renderer.domElement);
const scene = new Scene(); const camera = new PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer.setAnimationLoop((time) => { renderer.render(scene, camera); });
### Constructor Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `canvas` | `HTMLCanvasElement` | new canvas | Existing canvas element |
| `context` | `WebGLRenderingContext` | new context | Existing WebGL context |
| `precision` | `'highp' \| 'mediump' \| 'lowp'` | `'highp'` | Shader precision |
| `alpha` | `boolean` | `false` | Transparent canvas background |
| `premultipliedAlpha` | `boolean` | `true` | Premultiplied alpha blending |
| `antialias` | `boolean` | `false` | MSAA anti-aliasing |
| `stencil` | `boolean` | `true` | Stencil buffer |
| `preserveDrawingBuffer` | `boolean` | `false` | Required for screenshots |
| `powerPreference` | `'high-performance' \| 'low-power' \| 'default'` | `'default'` | GPU selection hint |
| `failIfMajorPerformanceCaveat` | `boolean` | `false` | Fail if software renderer |
| `depth` | `boolean` | `true` | Depth buffer |
| `logarithmicDepthBuffer` | `boolean` | `false` | Fix Z-fighting for large scenes |
**NEVER** change `antialias` after construction -- it CANNOT be modified post-creation.
### Key Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `domElement` | `HTMLCanvasElement` | -- | The canvas. ALWAYS append to DOM |
| `shadowMap.enabled` | `boolean` | `false` | MUST set `true` for any shadows |
| `shadowMap.type` | `ShadowMapType` | `PCFShadowMap` | Shadow filtering algorithm |
| `toneMapping` | `ToneMapping` | `NoToneMapping` | HDR tone mapping algorithm |
| `toneMappingExposure` | `number` | `1` | Exposure for tone mapping |
| `outputColorSpace` | `string` | `SRGBColorSpace` | Output color space |
| `autoClear` | `boolean` | `true` | Clear before each render call |
| `sortObjects` | `boolean` | `true` | Automatic draw order sorting |
| `clippingPlanes` | `Plane[]` | `[]` | Global clipping planes |
| `localClippingEnabled` | `boolean` | `false` | Enable per-material clipping |
| `info` | `object` | -- | Render stats (calls, triangles, memory) |
### Critical Warnings
**NEVER** forget to cap pixel ratio -- uncapped `devicePixelRatio` on 3x+ screens causes 9x+ pixel load. ALWAYS use `Math.min(window.devicePixelRatio, 2)`.
**NEVER** change `shadowMap.type` after the first render -- it forces full shader recompilation. ALWAYS set it once at initialization.
**NEVER** use `preserveDrawingBuffer: true` in production render loops -- it disables buffer-swap optimizations. ONLY enable it when screenshots are needed.
**NEVER** set `near: 0` on cameras -- it causes Z-fighting everywhere. ALWAYS keep `far / near` ratio under 10,000.
**NEVER** forget `camera.updateProjectionMatrix()` after modifying `fov`, `aspect`, `near`, `far`, or `zoom` -- the projection matrix is NOT auto-updated.
**NEVER** use raw `requestAnimationFrame` in Three.js r160+ -- ALWAYS use `renderer.setAnimationLoop()` which handles WebXR sessions automatically.
**NEVER** skip `renderer.dispose()` on cleanup -- it leaks WebGL contexts, programs, textures, and framebuffers.
---
## Color Management
Three.js r160+ uses a linear workflow with automatic sRGB conversion on output.
### Color Space Rules
| Texture Type | colorSpace | Examples |
|-------------|-----------|----------|
| Color / albedo | `SRGBColorSpace` | Diffuse maps, emissive maps |
| Data | `LinearSRGBColorSpace` | Normal maps, roughness, metalness, AO, displacement |
**Rule:** Color textures are ALWAYS `SRGBColorSpace`. Data textures are ALWAYS `LinearSRGBColorSpace`. Mixing these up produces washed-out or over-saturated renders.
### Tone Mapping Decision Tree
| Constant | When to Use |
|----------|------------|
| `NoToneMapping` | Non-photorealistic rendering, UI overlays, unlit scenes |
| `LinearToneMapping` | Basic HDR clamping with minimal color transformation |
| `ReinhardToneMapping` | General-purpose, preserves color hues well |
| `CineonToneMapping` | Cinematic film stock look |
| `ACESFilmicToneMapping` | **Default choice for PBR.** Industry-standard filmic curve |
| `AgXToneMapping` | Better than ACES for saturated colors. Avoids ACES hue shift on bright blues/reds. (r160+) |
| `NeutralToneMapping` | When color accuracy is paramount, minimal artistic transformation |
**ALWAYS** use `ACESFilmicToneMapping` or `AgXToneMapping` for PBR workflows. `AgXToneMapping` is preferred when saturated colors must remain accurate.
---
## Shadow Map Configuration
```javascript
import { PCFSoftShadowMap } from 'three';
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = PCFSoftShadowMap;
| Type | Quality | Performance | Notes |
|---|---|---|---|
BasicShadowMap |
Low (hard edges) | Fastest | No filtering |
PCFShadowMap |
Medium | Medium | Default. Percentage-Closer Filtering |
PCFSoftShadowMap |
High (soft edges) | Slower | Bilinear PCF. Most popular choice |
VSMShadowMap |
High (very soft) | Slowest | Can exhibit light bleeding artifacts |
Render Loop and Resize
setAnimationLoop (preferred)
renderer.setAnimationLoop((time) => {
// time is DOMHighResTimeStamp in milliseconds
renderer.render(scene, camera);
});
// Stop the loop
renderer.setAnimationLoop(null);
Window Resize Handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
ALWAYS update camera.aspect AND call updateProjectionMatrix() before setSize().
Camera System
PerspectiveCamera
import { PerspectiveCamera } from 'three';
const camera = new PerspectiveCamera(
50, // fov (vertical, degrees)
window.innerWidth / window.innerHeight, // aspect ratio
0.1, // near
1000 // far
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
After modifying fov, aspect, near, far, or zoom, you MUST call camera.updateProjectionMatrix().
OrthographicCamera
import { OrthographicCamera } from 'three';
const frustumSize = 10;
const aspect = window.innerWidth / window.innerHeight;
const camera = new OrthographicCamera(
-frustumSize * aspect / 2, // left
frustumSize * aspect / 2, // right
frustumSize / 2, // top
-frustumSize / 2, // bottom
0.1, 1000
);
ALWAYS update all six frustum parameters (left, right, top, bottom, near, far) on resize, then call updateProjectionMatrix().
ArrayCamera
Renders multiple viewports in a single render() call. Each sub-camera has camera.viewport = new Vector4(x, y, width, height). Used for split-screen and VR stereo.
CubeCamera
Renders the scene from all 6 directions into a WebGLCubeRenderTarget. Used for dynamic environment maps. NEVER call cubeCamera.update() every frame for static environments -- it renders the scene 6 times per call.
Render Targets
import { WebGLRenderTarget } from 'three';
const renderTarget = new WebGLRenderTarget(1024, 1024);
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.setRenderTarget(null); // restore default framebuffer
// renderTarget.texture is now usable as a regular Texture
ALWAYS call renderTarget.dispose() when no longer needed.
Shader Compilation
// Synchronous -- blocks the thread
renderer.compile(scene, camera);
// Asynchronous -- non-blocking, prevents frame drops (r160+)
await renderer.compileAsync(scene, camera);
ALWAYS call compileAsync() after loading assets but before the first visible render to prevent jank from just-in-time shader compilation.
Viewport and Scissor
// Render to a sub-region of the canvas
renderer.setViewport(x, y, width, height);
renderer.setScissor(x, y, width, height);
renderer.setScissorTest(true);
renderer.render(scene, camera);
// Reset to full canvas
renderer.setScissorTest(false);
renderer.setViewport(0, 0, canvas.width, canvas.height);
Clipping Planes
import { Plane, Vector3 } from 'three';
// Global clipping (affects all objects)
renderer.clippingPlanes = [new Plane(new Vector3(0, -1, 0), 1)];
// Per-material clipping (MUST enable localClippingEnabled)
renderer.localClippingEnabled = true;
material.clippingPlanes = [plane];
material.clipIntersection = false; // false = union, true = intersection
material.clipShadows = true; // also clip shadow geometry
Cleanup and Disposal
renderer.setAnimationLoop(null);
renderer.dispose();
renderer.domElement.remove();
ALWAYS call dispose() when removing a renderer. This releases the WebGL context, all compiled shader programs, textures, and framebuffers.
WebGPU Renderer
Three.js includes an experimental WebGPURenderer with TSL (Three Shading Language) node-based materials. For WebGPU-specific guidance, see the threejs-impl-webgpu skill.
Reference Links
- references/methods.md -- Complete WebGLRenderer and Camera API signatures
- references/examples.md -- Working code examples
- references/anti-patterns.md -- What NOT to do