Three.js Best Practices
Comprehensive performance optimization guide for Three.js applications. Contains 100+ rules across 17 categories, prioritized by impact.
Based on official guidelines from Three.js llms branch maintained by mrdoob.
When to Apply
Reference these guidelines when:
- Setting up a new Three.js project
- Writing or reviewing Three.js code
- Optimizing performance or fixing memory leaks
- Working with custom shaders (GLSL or TSL)
- Implementing WebGPU features
- Building VR/AR experiences with WebXR
- Integrating physics engines
- Optimizing for mobile devices
Rule Categories by Priority
| Priority |
Category |
Impact |
Prefix |
| 0 |
Modern Setup & Imports |
FUNDAMENTAL |
setup- |
| 1 |
Memory Management & Dispose |
CRITICAL |
memory- |
| 2 |
Render Loop Optimization |
CRITICAL |
render- |
| 3 |
Geometry & Buffer Management |
HIGH |
geometry- |
| 4 |
Material & Texture Optimization |
HIGH |
material- |
| 5 |
Lighting & Shadows |
MEDIUM-HIGH |
lighting- |
| 6 |
Scene Graph Organization |
MEDIUM |
scene- |
| 7 |
Shader Best Practices (GLSL) |
MEDIUM |
shader- |
| 8 |
TSL (Three.js Shading Language) |
MEDIUM |
tsl- |
| 9 |
Loading & Assets |
MEDIUM |
loading- |
| 10 |
Camera & Controls |
LOW-MEDIUM |
camera- |
| 11 |
Animation System |
MEDIUM |
animation- |
| 12 |
Physics Integration |
MEDIUM |
physics- |
| 13 |
WebXR / VR / AR |
MEDIUM |
webxr- |
| 14 |
Audio |
LOW-MEDIUM |
audio- |
| 15 |
Mobile Optimization |
HIGH |
mobile- |
| 16 |
Production |
HIGH |
error-, migration- |
| 17 |
Debug & DevTools |
LOW |
debug- |
Quick Reference
0. Modern Setup (FUNDAMENTAL)
setup-use-import-maps - Use Import Maps, not old CDN scripts
setup-choose-renderer - WebGLRenderer (default) vs WebGPURenderer (TSL/compute)
setup-animation-loop - Use renderer.setAnimationLoop() not manual RAF
setup-basic-scene-template - Complete modern scene template
1. Memory Management (CRITICAL)
memory-dispose-geometry - Always dispose geometries
memory-dispose-material - Always dispose materials and textures
memory-dispose-textures - Dispose dynamically created textures
memory-dispose-render-targets - Always dispose WebGLRenderTarget
memory-dispose-recursive - Use recursive disposal for hierarchies
memory-dispose-on-unmount - Dispose in React cleanup/unmount
memory-renderer-dispose - Dispose renderer when destroying view
memory-reuse-objects - Reuse geometries and materials
2. Render Loop (CRITICAL)
render-single-raf - Single requestAnimationFrame loop
render-conditional - Render on demand for static scenes
render-delta-time - Use delta time for animations
render-avoid-allocations - Never allocate in render loop
render-cache-computations - Cache expensive computations
render-frustum-culling - Enable frustum culling
render-update-matrix-manual - Disable auto matrix updates for static objects
render-pixel-ratio - Limit pixel ratio to 2
render-antialias-wisely - Use antialiasing judiciously
3. Geometry (HIGH)
geometry-buffer-geometry - Always use BufferGeometry
geometry-merge-static - Merge static geometries
geometry-instanced-mesh - Use InstancedMesh for identical objects
geometry-lod - Use Level of Detail for complex models
geometry-index-buffer - Use indexed geometry
geometry-vertex-count - Minimize vertex count
geometry-attributes-typed - Use appropriate typed arrays
geometry-interleaved - Consider interleaved buffers
4. Materials & Textures (HIGH)
material-reuse - Reuse materials across meshes
material-simplest-sufficient - Use simplest material that works
material-texture-size-power-of-two - Power-of-two texture dimensions
material-texture-compression - Use compressed textures (KTX2/Basis)
material-texture-mipmaps - Enable mipmaps appropriately
material-texture-anisotropy - Use anisotropic filtering for floors
material-texture-atlas - Use texture atlases
material-avoid-transparency - Minimize transparent materials
material-onbeforecompile - Use onBeforeCompile for shader mods (or TSL)
5. Lighting & Shadows (MEDIUM-HIGH)
lighting-limit-lights - Minimize dynamic lights
lighting-bake-static - Bake lighting for static scenes
lighting-shadow-camera-tight - Fit shadow camera tightly
lighting-shadow-map-size - Choose appropriate shadow resolution
lighting-shadow-selective - Enable shadows selectively
lighting-shadow-cascade - Use CSM for large scenes
lighting-probe - Use Light Probes
6. Scene Graph (MEDIUM)
scene-group-objects - Use Groups for organization
scene-layers - Use Layers for selective rendering
scene-visible-toggle - Use visible flag, not add/remove
scene-flatten-static - Flatten static hierarchies
scene-name-objects - Name objects for debugging
scene-object-pooling - Use object pooling
7. Shaders GLSL (MEDIUM)
shader-precision - Use appropriate precision
shader-avoid-branching - Minimize branching
shader-precompute-cpu - Precompute on CPU
shader-avoid-discard - Avoid discard, use alphaTest
shader-texture-lod - Use textureLod for known mip levels
shader-uniform-arrays - Prefer uniform arrays
shader-varying-interpolation - Use flat when appropriate
shader-chunk-injection - Use Three.js shader chunks
8. TSL - Three.js Shading Language (MEDIUM)
tsl-why-use - Use TSL instead of onBeforeCompile
tsl-setup-webgpu - WebGPU setup for TSL
tsl-complete-reference - Full TSL type system and functions
tsl-material-slots - Material node properties reference
tsl-node-materials - Use NodeMaterial classes
tsl-basic-operations - Types, operations, swizzling
tsl-functions - Creating TSL functions with Fn()
tsl-conditionals - If, select, loops in TSL
tsl-textures - Textures and triplanar mapping
tsl-post-processing - bloom, blur, dof, ao
tsl-compute-shaders - GPGPU and compute operations
tsl-glsl-to-tsl - GLSL to TSL translation
9. Loading & Assets (MEDIUM)
loading-draco-compression - Use Draco for large meshes
loading-gltf-preferred - Use glTF format
gltf-loading-optimization - Full loader setup with DRACO/Meshopt/KTX2
loading-progress-feedback - Show loading progress
loading-async-await - Use async/await for loading
loading-lazy - Lazy load non-critical assets
loading-cache-assets - Enable caching
loading-dispose-unused - Unload unused assets
10. Camera & Controls (LOW-MEDIUM)
camera-near-far - Set tight near/far planes
camera-fov - Choose appropriate FOV
camera-controls-damping - Use damping for smooth controls
camera-resize-handler - Handle resize properly
camera-orbit-limits - Set orbit control limits
11. Animation (MEDIUM)
animation-system - AnimationMixer, blending, morph targets, skeletal
12. Physics (MEDIUM)
physics-integration - Rapier, Cannon-es integration patterns
13. WebXR (MEDIUM)
webxr-setup - VR/AR buttons, controllers, hit testing
14. Audio (LOW-MEDIUM)
audio-spatial - PositionalAudio, HRTF, spatial sound
15. Optimization (HIGH)
mobile-optimization - Mobile-specific optimizations and checklist
raycasting-optimization - BVH, layers, GPU picking
16. Production (HIGH)
error-handling-recovery - WebGL context loss and recovery
migration-checklist - Breaking changes by version
17. Debug (LOW)
debug-stats - Use Stats.js
debug-helpers - Use built-in helpers
debug-gui - Use lil-gui for tweaking
debug-renderer-info - Check renderer.info
debug-conditional - Remove debug code in production
How to Use
Read individual rule files for detailed explanations and code examples:
rules/setup-use-import-maps.md
rules/memory-dispose-geometry.md
rules/tsl-complete-reference.md
rules/mobile-optimization.md
Each rule file contains:
- Brief explanation of why it matters
- BAD code example with explanation
- GOOD code example with explanation
- Additional context and references
Key Patterns
Modern Import Maps
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
"three/tsl": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.tsl.js"
}
}
</script>
Proper Disposal
function disposeObject(obj) {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach(m => m.dispose());
} else {
obj.material.dispose();
}
}
}
TSL Basic Usage
import { texture, uv, color, time, sin } from 'three/tsl';
const material = new THREE.MeshStandardNodeMaterial();
material.colorNode = texture(map).mul(color(0xff0000));
material.colorNode = color(0x00ff00).mul(sin(time).mul(0.5).add(0.5));
Mobile Detection
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, isMobile ? 1.5 : 2));
1---2name: three-best-practices3description: Three.js performance optimization and best practices guidelines. Use when writing, reviewing, or optimizing Three.js code. Triggers on tasks involving 3D scenes, WebGL/WebGPU rendering, geometries, materials, textures, lighting, shaders, or TSL.4license: MIT5---67# Three.js Best Practices89Comprehensive performance optimization guide for Three.js applications. Contains 100+ rules across 17 categories, prioritized by impact.1011> Based on official guidelines from Three.js `llms` branch maintained by mrdoob.1213## When to Apply1415Reference these guidelines when:16- Setting up a new Three.js project17- Writing or reviewing Three.js code18- Optimizing performance or fixing memory leaks19- Working with custom shaders (GLSL or TSL)20- Implementing WebGPU features21- Building VR/AR experiences with WebXR22- Integrating physics engines23- Optimizing for mobile devices2425## Rule Categories by Priority2627| Priority | Category | Impact | Prefix |28|----------|----------|--------|--------|29| 0 | Modern Setup & Imports | FUNDAMENTAL | `setup-` |30| 1 | Memory Management & Dispose | CRITICAL | `memory-` |31| 2 | Render Loop Optimization | CRITICAL | `render-` |32| 3 | Geometry & Buffer Management | HIGH | `geometry-` |33| 4 | Material & Texture Optimization | HIGH | `material-` |34| 5 | Lighting & Shadows | MEDIUM-HIGH | `lighting-` |35| 6 | Scene Graph Organization | MEDIUM | `scene-` |36| 7 | Shader Best Practices (GLSL) | MEDIUM | `shader-` |37| 8 | TSL (Three.js Shading Language) | MEDIUM | `tsl-` |38| 9 | Loading & Assets | MEDIUM | `loading-` |39| 10 | Camera & Controls | LOW-MEDIUM | `camera-` |40| 11 | Animation System | MEDIUM | `animation-` |41| 12 | Physics Integration | MEDIUM | `physics-` |42| 13 | WebXR / VR / AR | MEDIUM | `webxr-` |43| 14 | Audio | LOW-MEDIUM | `audio-` |44| 15 | Mobile Optimization | HIGH | `mobile-` |45| 16 | Production | HIGH | `error-`, `migration-` |46| 17 | Debug & DevTools | LOW | `debug-` |4748## Quick Reference4950### 0. Modern Setup (FUNDAMENTAL)5152- `setup-use-import-maps` - Use Import Maps, not old CDN scripts53- `setup-choose-renderer` - WebGLRenderer (default) vs WebGPURenderer (TSL/compute)54- `setup-animation-loop` - Use `renderer.setAnimationLoop()` not manual RAF55- `setup-basic-scene-template` - Complete modern scene template5657### 1. Memory Management (CRITICAL)5859- `memory-dispose-geometry` - Always dispose geometries60- `memory-dispose-material` - Always dispose materials and textures61- `memory-dispose-textures` - Dispose dynamically created textures62- `memory-dispose-render-targets` - Always dispose WebGLRenderTarget63- `memory-dispose-recursive` - Use recursive disposal for hierarchies64- `memory-dispose-on-unmount` - Dispose in React cleanup/unmount65- `memory-renderer-dispose` - Dispose renderer when destroying view66- `memory-reuse-objects` - Reuse geometries and materials6768### 2. Render Loop (CRITICAL)6970- `render-single-raf` - Single requestAnimationFrame loop71- `render-conditional` - Render on demand for static scenes72- `render-delta-time` - Use delta time for animations73- `render-avoid-allocations` - Never allocate in render loop74- `render-cache-computations` - Cache expensive computations75- `render-frustum-culling` - Enable frustum culling76- `render-update-matrix-manual` - Disable auto matrix updates for static objects77- `render-pixel-ratio` - Limit pixel ratio to 278- `render-antialias-wisely` - Use antialiasing judiciously7980### 3. Geometry (HIGH)8182- `geometry-buffer-geometry` - Always use BufferGeometry83- `geometry-merge-static` - Merge static geometries84- `geometry-instanced-mesh` - Use InstancedMesh for identical objects85- `geometry-lod` - Use Level of Detail for complex models86- `geometry-index-buffer` - Use indexed geometry87- `geometry-vertex-count` - Minimize vertex count88- `geometry-attributes-typed` - Use appropriate typed arrays89- `geometry-interleaved` - Consider interleaved buffers9091### 4. Materials & Textures (HIGH)9293- `material-reuse` - Reuse materials across meshes94- `material-simplest-sufficient` - Use simplest material that works95- `material-texture-size-power-of-two` - Power-of-two texture dimensions96- `material-texture-compression` - Use compressed textures (KTX2/Basis)97- `material-texture-mipmaps` - Enable mipmaps appropriately98- `material-texture-anisotropy` - Use anisotropic filtering for floors99- `material-texture-atlas` - Use texture atlases100- `material-avoid-transparency` - Minimize transparent materials101- `material-onbeforecompile` - Use onBeforeCompile for shader mods (or TSL)102103### 5. Lighting & Shadows (MEDIUM-HIGH)104105- `lighting-limit-lights` - Minimize dynamic lights106- `lighting-bake-static` - Bake lighting for static scenes107- `lighting-shadow-camera-tight` - Fit shadow camera tightly108- `lighting-shadow-map-size` - Choose appropriate shadow resolution109- `lighting-shadow-selective` - Enable shadows selectively110- `lighting-shadow-cascade` - Use CSM for large scenes111- `lighting-probe` - Use Light Probes112113### 6. Scene Graph (MEDIUM)114115- `scene-group-objects` - Use Groups for organization116- `scene-layers` - Use Layers for selective rendering117- `scene-visible-toggle` - Use visible flag, not add/remove118- `scene-flatten-static` - Flatten static hierarchies119- `scene-name-objects` - Name objects for debugging120- `scene-object-pooling` - Use object pooling121122### 7. Shaders GLSL (MEDIUM)123124- `shader-precision` - Use appropriate precision125- `shader-avoid-branching` - Minimize branching126- `shader-precompute-cpu` - Precompute on CPU127- `shader-avoid-discard` - Avoid discard, use alphaTest128- `shader-texture-lod` - Use textureLod for known mip levels129- `shader-uniform-arrays` - Prefer uniform arrays130- `shader-varying-interpolation` - Use flat when appropriate131- `shader-chunk-injection` - Use Three.js shader chunks132133### 8. TSL - Three.js Shading Language (MEDIUM)134135- `tsl-why-use` - Use TSL instead of onBeforeCompile136- `tsl-setup-webgpu` - WebGPU setup for TSL137- `tsl-complete-reference` - Full TSL type system and functions138- `tsl-material-slots` - Material node properties reference139- `tsl-node-materials` - Use NodeMaterial classes140- `tsl-basic-operations` - Types, operations, swizzling141- `tsl-functions` - Creating TSL functions with Fn()142- `tsl-conditionals` - If, select, loops in TSL143- `tsl-textures` - Textures and triplanar mapping144- `tsl-post-processing` - bloom, blur, dof, ao145- `tsl-compute-shaders` - GPGPU and compute operations146- `tsl-glsl-to-tsl` - GLSL to TSL translation147148### 9. Loading & Assets (MEDIUM)149150- `loading-draco-compression` - Use Draco for large meshes151- `loading-gltf-preferred` - Use glTF format152- `gltf-loading-optimization` - Full loader setup with DRACO/Meshopt/KTX2153- `loading-progress-feedback` - Show loading progress154- `loading-async-await` - Use async/await for loading155- `loading-lazy` - Lazy load non-critical assets156- `loading-cache-assets` - Enable caching157- `loading-dispose-unused` - Unload unused assets158159### 10. Camera & Controls (LOW-MEDIUM)160161- `camera-near-far` - Set tight near/far planes162- `camera-fov` - Choose appropriate FOV163- `camera-controls-damping` - Use damping for smooth controls164- `camera-resize-handler` - Handle resize properly165- `camera-orbit-limits` - Set orbit control limits166167### 11. Animation (MEDIUM)168169- `animation-system` - AnimationMixer, blending, morph targets, skeletal170171### 12. Physics (MEDIUM)172173- `physics-integration` - Rapier, Cannon-es integration patterns174175### 13. WebXR (MEDIUM)176177- `webxr-setup` - VR/AR buttons, controllers, hit testing178179### 14. Audio (LOW-MEDIUM)180181- `audio-spatial` - PositionalAudio, HRTF, spatial sound182183### 15. Optimization (HIGH)184185- `mobile-optimization` - Mobile-specific optimizations and checklist186- `raycasting-optimization` - BVH, layers, GPU picking187188### 16. Production (HIGH)189190- `error-handling-recovery` - WebGL context loss and recovery191- `migration-checklist` - Breaking changes by version192193### 17. Debug (LOW)194195- `debug-stats` - Use Stats.js196- `debug-helpers` - Use built-in helpers197- `debug-gui` - Use lil-gui for tweaking198- `debug-renderer-info` - Check renderer.info199- `debug-conditional` - Remove debug code in production200201## How to Use202203Read individual rule files for detailed explanations and code examples:204205```206rules/setup-use-import-maps.md207rules/memory-dispose-geometry.md208rules/tsl-complete-reference.md209rules/mobile-optimization.md210```211212Each rule file contains:213- Brief explanation of why it matters214- BAD code example with explanation215- GOOD code example with explanation216- Additional context and references217218## Key Patterns219220### Modern Import Maps221222```html223<script type="importmap">224{225 "imports": {226 "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",227 "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",228 "three/tsl": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.tsl.js"229 }230}231</script>232```233234### Proper Disposal235236```javascript237function disposeObject(obj) {238 if (obj.geometry) obj.geometry.dispose();239 if (obj.material) {240 if (Array.isArray(obj.material)) {241 obj.material.forEach(m => m.dispose());242 } else {243 obj.material.dispose();244 }245 }246}247```248249### TSL Basic Usage250251```javascript252import { texture, uv, color, time, sin } from 'three/tsl';253254const material = new THREE.MeshStandardNodeMaterial();255material.colorNode = texture(map).mul(color(0xff0000));256material.colorNode = color(0x00ff00).mul(sin(time).mul(0.5).add(0.5));257```258259### Mobile Detection260261```javascript262const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);263renderer.setPixelRatio(Math.min(window.devicePixelRatio, isMobile ? 1.5 : 2));264```