Jesse A. Green / notebook

three.js Administrator & Developer Guide

Generated May 2026 from mrdoob/three.js at 477da61

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide covers the architecture, internals, build systems, performance tuning, and contribution workflow for three.js v0.184.0 (revision r184). It is written for developers maintaining, extending, or deploying applications built on three.js. For usage tutorials, installation walkthroughs, and feature overviews, see the User Guide.


1. Architecture Overview

three.js is a JavaScript 3D graphics library that creates and renders 3D scenes using WebGL and WebGPU. The library follows a scene-graph architecture where all renderable objects inherit from Object3D and are organized in a tree rooted at a Scene instance. A Camera defines the viewpoint, and a Renderer traverses the scene graph to produce frames.

1.1 High-Level Architecture

Application Code
       │
       ▼
┌──────────────────────────────────────────────┐
│  Scene Graph (Object3D tree)                 │
│  ├── Mesh (BufferGeometry + Material)        │
│  ├── Light (DirectionalLight, PointLight...) │
│  ├── Camera (PerspectiveCamera, Ortho...)    │
│  ├── Group, LOD, InstancedMesh, BatchedMesh  │
│  └── Audio, Sprite, Line, Points, Bone...    │
└──────────────────────────────┬───────────────┘
                               │
                    ┌──────────▼──────────┐
                    │     Renderer        │
                    │  ┌───────────────┐  │
                    │  │ WebGLRenderer │  │  (Legacy path)
                    │  └───────────────┘  │
                    │  ┌───────────────┐  │
                    │  │ WebGPURenderer│  │  (Modern path)
                    │  │  ├─ WebGPU   │  │
                    │  │  └─ WebGL    │  │  (fallback backend)
                    │  └───────────────┘  │
                    └─────────────────────┘
                               │
                    ┌──────────▼──────────┐
                    │   GPU / Canvas      │
                    └─────────────────────┘

1.2 Dual Renderer Strategy

three.js maintains two rendering paths:

Path Entry Renderer Shader Language Status
Legacy three (default import) WebGLRenderer GLSL Stable, widely deployed
Modern three/webgpu WebGPURenderer WGSL (via TSL) Active development

The modern path uses a backend abstraction layer (src/renderers/common/) that allows WebGPURenderer to run on either the native WebGPU API or fall back to WebGL via WebGLBackend. This is distinct from the legacy WebGLRenderer, which directly manages WebGL state.

1.3 Core Design Patterns

  • Scene graph: Tree of Object3D nodes with parent/child relationships, local and world transforms
  • Component composition: Objects combine geometry (BufferGeometry) with appearance (Material)
  • Event system: EventDispatcher mixin provides addEventListener/removeEventListener/dispatchEvent on all major classes
  • Clone/copy pattern: Every class implements clone() (returns new instance) and copy() (copies into existing instance)
  • Serialization: toJSON() on Object3D, Material, BufferGeometry, Texture produces a portable JSON format; ObjectLoader reconstructs
  • Dispose pattern: GPU resources require explicit dispose() calls (geometry, material, texture, render target, renderer)
  • DataMap pattern: The common renderer infrastructure uses DataMap instances to associate internal GPU state with user-facing objects without polluting their properties

Note

three.js has zero runtime dependencies. All 5,875 source files are self-contained. Every package listed in package.json is a devDependency used only for building, testing, or documentation generation.

1.4 Module Organization

The library is organized into a strict module hierarchy:

src/
├── Three.Core.js        ← Barrel export for the WebGL path (default "three" import)
├── Three.TSL.js         ← Re-exports for the TSL node shading language
├── Three.WebGPU.js      ← Barrel export for WebGPU path ("three/webgpu")
├── constants.js         ← All enumerations and constant values
└── utils.js             ← Console output helpers (warn/error deduplication)

The exports field in package.json defines the public API surface:

Import Path Resolves To Purpose
three build/three.module.js Core library (WebGL renderer)
three/webgpu build/three.webgpu.js WebGPU renderer + TSL
three/tsl build/three.tsl.js TSL shader language only
three/addons examples/jsm/Addons.js All addon modules
three/addons/* examples/jsm/* Individual addons
three/src/* src/* Raw source modules

2. System Requirements

2.1 Runtime Requirements

Requirement Minimum Recommended
Browser (WebGL) Chrome 80+, Firefox 78+, Safari 14+, Edge 80+ Latest stable
Browser (WebGPU) Chrome 113+, Firefox 141+ (behind flag), Safari 18+ Chrome latest
WebGL version WebGL 2.0 WebGL 2.0
JavaScript ES2015 (ES6) modules ES2022
Node.js (SSR/build) 14+ 18+ LTS

Warning

WebGL 1 has not been supported since r163. Attempting to create a WebGLRenderer in a WebGL 1-only context will throw: "THREE.WebGLRenderer: WebGL 1 is not supported since r163." There is no fallback.

2.2 Development Requirements

Tool Version Purpose
Node.js 18+ Build, test, dev server
npm 8+ Package management
Rollup 4.6+ Module bundling
ESLint 9.0+ Code linting
QUnit 2.19+ Unit testing
Puppeteer 24+ E2E screenshot testing
JSDoc 4.0+ Documentation generation

2.3 Hardware Considerations

Scenario GPU Notes
Basic scenes Integrated GPU Adequate for simple geometry, few lights
Complex scenes Dedicated GPU Required for heavy instancing, post-processing, shadows
WebGPU compute Modern discrete GPU Compute shaders require WebGPU-capable hardware
Mobile Varies Reduce draw calls, texture sizes, shadow map resolution

3. Configuration Reference

three.js has no configuration files. All configuration is programmatic, passed through constructor options and property assignment.

Important

There are zero extractable config keys. Configuration is entirely programmatic through renderer constructor options, material properties, and scene settings. The subsections below document every configurable surface.

3.1 WebGLRenderer Constructor Options

const renderer = new THREE.WebGLRenderer({
  canvas,                       // HTMLCanvasElement — existing canvas to render into
  context,                      // WebGL2RenderingContext — existing GL context
  precision,                    // "highp" | "mediump" | "lowp" — shader precision
  alpha,                        // boolean — transparent canvas (default false)
  premultipliedAlpha,           // boolean — premultiplied alpha (default true)
  antialias,                    // boolean — MSAA (default false)
  stencil,                      // boolean — stencil buffer (default true)
  preserveDrawingBuffer,        // boolean — keep frame buffer for screenshots (default false)
  powerPreference,              // "default" | "high-performance" | "low-power"
  failIfMajorPerformanceCaveat, // boolean — fail if software rendering (default false)
  logarithmicDepthBuffer        // boolean — logarithmic depth (default false)
});

3.2 WebGLRenderer Runtime Properties

Property Type Default Description
shadowMap.enabled boolean false Enable shadow mapping
shadowMap.type constant PCFShadowMap BasicShadowMap, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap
toneMapping constant NoToneMapping LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping
toneMappingExposure number 1.0 Exposure level for tone mapping
outputColorSpace string SRGBColorSpace Output color space
autoClear boolean true Clear before each render
autoClearColor boolean true Clear color buffer
autoClearDepth boolean true Clear depth buffer
autoClearStencil boolean true Clear stencil buffer
sortObjects boolean true Sort transparent objects
localClippingEnabled boolean false Enable per-material clipping planes

3.3 WebGPURenderer Initialization

import { WebGPURenderer } from 'three/webgpu';

const renderer = new WebGPURenderer();
await renderer.init();  // REQUIRED — async initialization
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);

Important

WebGPURenderer requires asynchronous initialization via await renderer.init(). Calling render() before init() completes will throw an error. The WebGPU path also falls back to WebGLBackend automatically when WebGPU is unavailable.

3.4 Scene Configuration

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);    // Color, Texture, or null
scene.environment = envTexture;                   // Texture for IBL (image-based lighting)
scene.fog = new THREE.Fog(0xcccccc, 10, 100);    // Linear fog: color, near, far
scene.fog = new THREE.FogExp2(0xcccccc, 0.002);  // Exponential fog: color, density
scene.backgroundBlurriness = 0.0;                // 0-1, blur background texture
scene.backgroundIntensity = 1.0;                 // Background brightness multiplier
scene.backgroundRotation = new THREE.Euler();    // Rotate background
scene.overrideMaterial = material;               // Force all objects to use this material

3.5 Constants and Enumerations

All constants are defined in src/constants.js. Key groups:

Group Constants
Side FrontSide, BackSide, DoubleSide
Blending NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending
Depth NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth
Texture Wrapping RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping
Texture Filtering NearestFilter, LinearFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapNearestFilter, LinearMipmapLinearFilter
Texture Format AlphaFormat, RGBAFormat, RGBFormat, RedFormat, RGFormat, RedIntegerFormat, RGIntegerFormat, RGBAIntegerFormat, DepthFormat, DepthStencilFormat
Texture Type UnsignedByteType, FloatType, HalfFloatType, UnsignedIntType, IntType, ByteType, ShortType, UnsignedShortType
Encoding SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace
Shadow Map BasicShadowMap, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap
Tone Mapping NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping
Coordinate System WebGLCoordinateSystem, WebGPUCoordinateSystem

3.6 Material Configuration Matrix

Common properties shared across all material types:

Property Type Default Description
color Color 0xffffff Base color
map Texture null Color map texture
opacity number 1.0 Overall opacity (requires transparent: true for <1)
transparent boolean false Enable alpha blending
side constant FrontSide Which face sides to render
depthTest boolean true Enable depth testing
depthWrite boolean true Write to depth buffer
blending constant NormalBlending Blending mode
wireframe boolean false Render as wireframe
flatShading boolean false Flat (faceted) shading
fog boolean true Affected by scene fog
visible boolean true Whether to render

MeshPhysicalMaterial advanced PBR properties (extends MeshStandardMaterial):

Property Type Default Description
clearcoat number 0.0 Clearcoat layer intensity
clearcoatRoughness number 0.0 Clearcoat roughness
transmission number 0.0 Optical transmission (glass)
ior number 1.5 Index of refraction
thickness number 0.0 Volume thickness for transmission
attenuationColor Color 0xffffff Volume absorption color
attenuationDistance number Infinity Volume absorption distance
sheen number 0.0 Sheen layer intensity
sheenColor Color 0x000000 Sheen tint
sheenRoughness number 1.0 Sheen roughness
iridescence number 0.0 Iridescence intensity
anisotropy number 0.0 Anisotropic reflection strength
dispersion number 0.0 Chromatic dispersion
specularIntensity number 1.0 Specular reflection strength
specularColor Color 0xffffff Specular tint

4. Deployment & Scaling

4.1 Bundling for Production

three.js is published as ES modules. The recommended build approach:

// Import only what you need — tree-shaking removes the rest
import { Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry,
         MeshStandardMaterial, Mesh, DirectionalLight } from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

Rollup (used by three.js itself):

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';

export default {
  input: 'src/main.js',
  output: { file: 'dist/bundle.js', format: 'es' },
  plugins: [resolve(), terser()]
};

Webpack, Vite, esbuild, and Parcel all support three.js out of the box via the module and exports fields in package.json.

4.2 Tree-Shaking

three.js declares "sideEffects": ["./src/nodes/**/*"] in package.json. This tells bundlers that all modules except the node system are side-effect-free and can be safely eliminated when unused.

The tree-shaking test (npm run test-treeshake) verifies that importing individual classes does not pull in the entire library:

npm run test-treeshake   # rollup -c test/rollup.treeshake.config.js

Best practices for minimal bundles:

  1. Import from three (not three/src/*) for optimized builds
  2. Import addons individually: three/addons/controls/OrbitControls.js rather than three/addons
  3. Avoid importing three/webgpu unless WebGPU features are needed (pulls in the entire node system)
  4. Use MeshBasicMaterial or MeshLambertMaterial when PBR is unnecessary

4.3 CDN Deployment

For quick prototyping or non-bundled deployments:

<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/"
  }
}
</script>
<script type="module">
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>

Warning

Always pin a specific version when using CDNs (@0.184.0). three.js frequently has breaking changes between releases. The latest tag can silently break production applications.

4.4 Multiple Instances Detection

three.js detects when multiple copies are loaded and emits a console warning. This commonly occurs when:

  • Dependencies bundle their own copy of three.js
  • Import maps resolve to a different URL than node_modules
  • Both CJS (three.cjs) and ESM (three.module.js) are loaded

The detection uses the global __THREE_DEVTOOLS__ symbol.

4.5 Performance Scaling Strategies

Technique When to Use Implementation
InstancedMesh 100+ identical objects Single draw call for all instances
BatchedMesh Multiple geometries, fewer draw calls Multi-draw batching API
LOD Large scenes with distant objects Automatic detail level switching
WebGPURenderer Compute-heavy, modern browsers Async init, compute shaders
Texture atlasing Many small textures Combine into single texture, adjust UVs
Geometry merging Static scenes BufferGeometryUtils.mergeGeometries()
Frustum culling Default (automatic) object.frustumCulled = true (default)
Layers Selective rendering 32-layer bitmask on objects and cameras

5. Operations

5.1 Build System

three.js uses Rollup for building distributable bundles.

# Full build (all formats)
npm run build
# → rollup -c utils/build/rollup.config.js
# Produces: build/three.cjs, build/three.module.js,
#           build/three.webgpu.js, build/three.tsl.js

# ES module only (faster)
npm run build-module
# → rollup -c utils/build/rollup.config.js --configOnlyModule

The build configuration lives at utils/build/rollup.config.js and uses:

  • @rollup/plugin-node-resolve for module resolution
  • @rollup/plugin-terser for minification

Tip

During development, use npm start (alias: npm run dev) which runs node utils/build/dev.js && node utils/server.js -p 8080. This builds incrementally and starts a local development server on port 8080.

5.2 Development Server

npm start
# Equivalent to: node utils/build/dev.js && node utils/server.js -p 8080

The dev server (utils/server.js) serves the project root, making examples accessible at http://localhost:8080/examples/.

5.3 Testing

three.js has three testing tiers:

Unit Tests (QUnit)

# Core library tests
npm run test-unit
# → qunit test/unit/three.source.unit.js

# Addon tests
npm run test-unit-addons
# → qunit test/unit/three.addons.unit.js

# Both (plus lint)
npm test
# → npm run lint && npm run test-unit && npm run test-unit-addons

Unit tests live in test/unit/ and follow the same directory structure as src/. Each class has a corresponding test file.

E2E Screenshot Tests (Puppeteer)

# Run against WebGL
npm run test-e2e
# → node test/e2e/puppeteer.js

# Run against WebGPU
npm run test-e2e-webgpu
# → node test/e2e/puppeteer.js --webgpu

# Generate new reference screenshots
npm run make-screenshot
# → node test/e2e/puppeteer.js --make

E2E tests render examples with Puppeteer and compare screenshots pixel-by-pixel using pngjs and jpeg-js. Reference images are stored alongside tests.

Tree-Shaking Verification

npm run test-treeshake
# → rollup -c test/rollup.treeshake.config.js

Validates that tree-shaking works correctly and unused code is eliminated.

Note

The full test suite (npm test) runs linting first, then unit tests. E2E tests are separate because they require a running browser and are slower. CI pipelines should run npm test for fast feedback and npm run test-e2e as a secondary step.

5.4 Linting

# Lint core source
npm run lint
# → npm run lint-core (eslint src)

# Auto-fix all directories
npm run lint-fix

ESLint configuration (eslint.config.js):

  • ECMAScript 2022, source type: module
  • Browser + Node + ES2017 globals
  • Custom globals for three.js ecosystem: __THREE_DEVTOOLS__, potpack, fflate, Stats, GPU/XR prefixed globals
  • Key enforced rules:
    • no-throw-literal: error — must throw Error objects
    • quotes: single — single quotes everywhere
    • prefer-const — use const when variable is never reassigned
    • no-duplicate-imports — combine imports from same module
    • prefer-spread — use spread over .apply()
  • JSDoc rules: check-types: error, require-param-type: error, require-returns-type: error
  • Ignored directories: node_modules, build, libs (vendored: acorn, codemirror, ternjs, esprima, jsonlint, signals), 3rdparty, vendor

5.5 Documentation Generation

# Build API docs + LLM-friendly docs
npm run build-docs
# → jsdoc -c utils/docs/jsdoc.config.json && npm run build-llms

# LLM docs only
npm run build-llms
# → node utils/llms/build.js

The API documentation site source lives in docs/ and is built with JSDoc using the config at utils/docs/jsdoc.config.json. The build-llms step generates LLM-optimized documentation via utils/llms/build.js.

5.6 Preview Builds

npm run preview
# → node utils/build/preview.js

Generates preview builds for testing changes before a full build.


6. Performance

6.1 Draw Call Optimization

Draw calls are the primary performance bottleneck in WebGL/WebGPU rendering. three.js provides several mechanisms to reduce them:

InstancedMesh — renders many copies of the same geometry+material in a single draw call:

const mesh = new THREE.InstancedMesh(geometry, material, count);
// Set transforms for each instance
const matrix = new THREE.Matrix4();
for (let i = 0; i < count; i++) {
  matrix.setPosition(x, y, z);
  mesh.setMatrixAt(i, matrix);
}
mesh.instanceMatrix.needsUpdate = true;

// Optional per-instance color
mesh.setColorAt(i, new THREE.Color(0xff0000));
mesh.instanceColor.needsUpdate = true;

BatchedMesh — multi-draw batching for mixed geometries:

const batchedMesh = new BatchedMesh(maxInstanceCount, maxVertexCount, maxIndexCount);
const geoId = batchedMesh.addGeometry(geometry);
const instanceId = batchedMesh.addInstance(geoId);
batchedMesh.setMatrixAt(instanceId, matrix);

Caution

BatchedMesh enforces strict limits. Exceeding maxInstanceCount, maxVertexCount, or maxIndexCount throws validation errors. Pre-calculate your maximums and add headroom. Error messages include: "THREE.BatchedMesh: Maximum item count reached" and "THREE.BatchedMesh: %.reservedSpace exceeded".

Geometry Merging — for completely static scenes:

import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geo1, geo2, geo3]);
// Results in a single Mesh with one draw call

6.2 Memory Management

three.js does not automatically release GPU resources. Every GPU-backed object must be explicitly disposed:

// Geometry
geometry.dispose();

// Material (and its textures)
material.dispose();
material.map?.dispose();
material.normalMap?.dispose();
material.envMap?.dispose();
// ... all texture properties

// Render targets
renderTarget.dispose();

// Renderer (releases all GPU resources)
renderer.dispose();

Common memory leak pattern:

// BAD: Creates new geometry every frame
function animate() {
  const geo = new THREE.BoxGeometry(1, 1, 1);
  mesh.geometry = geo;  // Old geometry leaked!
}

// GOOD: Reuse or dispose
function animate() {
  mesh.geometry.dispose();
  mesh.geometry = new THREE.BoxGeometry(1, 1, 1);
}

6.3 Texture Optimization

Technique Impact Implementation
Power-of-two textures Avoids GPU padding Use 256, 512, 1024, 2048 px dimensions
Compressed textures 4-8x memory reduction Use KTX2Loader with Basis Universal
Mipmaps Better minification quality Enabled by default for power-of-two
texture.generateMipmaps Save VRAM for UI textures Set false for non-3D textures
texture.anisotropy Quality vs. performance Set to renderer.capabilities.getMaxAnisotropy() for best quality

6.4 Shadow Map Performance

Shadow mapping is one of the most expensive operations. Configuration choices significantly impact performance:

Shadow Map Type Quality Cost Notes
BasicShadowMap Low Lowest Hard edges, fast
PCFShadowMap Medium Medium Default, filtered edges
PCFSoftShadowMap High High Softer edges, more samples
VSMShadowMap High Highest Variance shadow maps, supports blurring

Optimization strategies:

  • Limit shadow.mapSize (512 for distant lights, 2048 maximum for hero lights)
  • Set tight shadow.camera.near/shadow.camera.far bounds
  • Disable shadows on objects that don't need them (castShadow = false, receiveShadow = false)
  • Use shadow.autoUpdate = false for static scenes, call shadow.needsUpdate = true only when the scene changes

6.5 Frustum Culling and Layers

Frustum culling is enabled by default (object.frustumCulled = true). Objects outside the camera's view frustum are skipped during rendering. For objects with expensive bounding sphere calculations or known visibility, disable selectively.

Layers provide a 32-channel bitmask system for selective rendering and raycasting:

// Assign object to layer 1
mesh.layers.set(1);

// Camera sees only layer 1
camera.layers.set(1);

// Enable multiple layers
camera.layers.enable(0);
camera.layers.enable(1);

// Raycaster respects layers
raycaster.layers.set(1);

6.6 Level of Detail (LOD)

const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0);     // Used when distance < 50
lod.addLevel(mediumDetailMesh, 50);  // Used when distance < 200
lod.addLevel(lowDetailMesh, 200);    // Used when distance >= 200
scene.add(lod);

LOD automatically switches between detail levels based on camera distance, computed each frame during lod.update(camera).

6.7 Animation Performance

  • Use AnimationMixer.update(delta) with the actual frame delta, not a fixed timestep
  • Disable animations when objects are off-screen
  • For large numbers of animated objects, consider skeletal animation with SkinnedMesh/Bone/Skeleton over per-vertex animation
  • AnimationAction.setEffectiveWeight(0) stops a blended action from consuming CPU without removing it

6.8 WebGPU Performance Advantages

The WebGPU path offers performance improvements over WebGL for specific workloads:

  • Compute shaders: Offload particle systems, physics, or mesh operations to the GPU via renderer.compute()
  • Reduced CPU overhead: Better command batching and pipeline state caching
  • Render bundles: RenderBundle reduces per-frame JavaScript overhead for static scene portions

7. Security

7.1 WebGL/WebGPU Context Security

Warning

WebGL and WebGPU execute code on the GPU. While browser sandboxing limits direct system access, several attack vectors exist that three.js applications should mitigate.

Context loss: WebGL contexts can be lost due to GPU driver crashes, system resource pressure, or malicious pages. Handle gracefully:

renderer.domElement.addEventListener('webglcontextlost', (event) => {
  event.preventDefault();
  // Pause rendering, show user message
});

renderer.domElement.addEventListener('webglcontextrestored', () => {
  // Reinitialize resources
});

7.2 Cross-Origin Resource Loading

three.js loaders fetch external resources (textures, models, audio). Cross-origin requests are subject to CORS:

const loader = new THREE.TextureLoader();
loader.crossOrigin = 'anonymous';  // Required for cross-origin textures

// For loaders using LoadingManager
const manager = new THREE.LoadingManager();
manager.crossOrigin = 'anonymous';

Security considerations:

  • Textures loaded cross-origin without CORS headers will taint the canvas, preventing toDataURL() and readPixels()
  • preserveDrawingBuffer: true is required for screenshots but exposes frame content to JavaScript
  • Model files (GLTF, OBJ, FBX) can reference external textures — validate URLs in user-submitted content

7.3 User-Generated Content

When loading user-provided 3D models or scenes:

Warning

ObjectLoader.parse() can reconstruct arbitrary three.js scenes from JSON, including custom shader code in ShaderMaterial. Never load untrusted scene JSON without sandboxing.

  • Shader injection: ShaderMaterial and RawShaderMaterial accept arbitrary GLSL/WGSL. Sanitize or reject user-provided shader code
  • Resource exhaustion: Malicious models can contain extremely high polygon counts or massive textures. Validate geometry vertex counts and texture dimensions before loading
  • External references: GLTF files can embed data: URIs or reference external URLs. Audit loaded resources
  • Script execution: The three.js Editor supports embedded scripts via its script editor (using esprima for parsing). Never expose the editor to untrusted users without sandboxing

7.4 Timing Attacks

WebGL's readPixels() and timing of draw calls can theoretically leak cross-origin image data. Modern browsers mitigate this, but avoid rendering sensitive cross-origin content on the same canvas as user-controlled shaders.


8. Developer Internals

8.1 Repository Structure

three.js/
├── src/                         Core library source (ES modules)
│   ├── Three.Core.js            Main barrel export (WebGL path)
│   ├── Three.TSL.js             TSL node system re-exports
│   ├── Three.WebGPU.js          WebGPU path barrel export
│   ├── constants.js             All enumerations
│   ├── utils.js                 Console helpers
│   ├── core/                    Object3D, BufferGeometry, EventDispatcher, etc.
│   ├── renderers/               Rendering engines
│   │   ├── WebGLRenderer.js     Legacy WebGL renderer (~3000 lines)
│   │   ├── common/              Shared renderer infrastructure
│   │   │   ├── Renderer.js      Base class for modern path
│   │   │   ├── Backend.js       GPU backend abstraction
│   │   │   ├── RenderList.js    Render list management
│   │   │   ├── RenderObject.js  Per-object render state
│   │   │   ├── RenderBundle.js  Render bundle support
│   │   │   ├── RenderContext.js Render pass context
│   │   │   ├── DataMap.js       Internal state association
│   │   │   └── PostProcessing.js Post-processing support
│   │   ├── webgl-fallback/      WebGL backend for modern path
│   │   │   └── WebGLBackend.js  WebGL backend implementation
│   │   └── webgpu/              WebGPU backend
│   │       └── WebGPUBackend.js WebGPU backend implementation
│   ├── scenes/                  Scene, Fog, FogExp2
│   ├── cameras/                 Camera types
│   ├── lights/                  Light types + shadow classes
│   ├── materials/               All material types
│   ├── geometries/              All geometry types
│   ├── objects/                 Mesh, Line, Points, Sprite, etc.
│   ├── animation/               AnimationMixer, Clip, Action, Track
│   ├── loaders/                 Loader system + LoadingManager
│   ├── audio/                   Web Audio integration
│   ├── math/                    Vector, Matrix, Quaternion, etc.
│   ├── textures/                Texture types
│   ├── helpers/                 Visual debugging aids
│   ├── extras/                  Controls, Curves, Path, PMREMGenerator
│   └── nodes/                   Node-based shader system (TSL)
│       ├── core/                40+ node types
│       ├── utils/               Utility nodes
│       └── materialx/           MaterialX integration
├── examples/
│   ├── jsm/                     Addon modules (published as three/addons)
│   │   ├── controls/            OrbitControls, FlyControls, etc.
│   │   ├── loaders/             GLTFLoader, DRACOLoader, etc.
│   │   ├── exporters/           GLTFExporter, OBJExporter, etc.
│   │   ├── postprocessing/      EffectComposer, passes
│   │   ├── utils/               BufferGeometryUtils, SceneOptimizer, etc.
│   │   ├── Addons.js            Barrel export for all addons
│   │   └── ...                  Materials, geometries, curves, etc.
│   ├── fonts/                   Font data files (published as three/examples/fonts)
│   ├── models/                  Example 3D models
│   ├── shaders/                 Example shader code
│   └── textures/                Example textures
├── editor/                      Web-based 3D editor
│   ├── index.html               Editor entry point
│   ├── js/                      Editor JavaScript
│   │   ├── Editor.js            Core editor state
│   │   ├── commands/            24 undo/redo command classes
│   │   ├── libs/                UI framework, codemirror, esprima
│   │   └── ...                  Viewport, sidebar, menubar, etc.
│   ├── css/                     Editor styles
│   └── examples/                Editor example scenes
├── devtools/                    Browser devtools extension
├── manual/                      Tutorial content (multi-language)
├── docs/                        API documentation site source
├── test/
│   ├── unit/                    QUnit unit tests
│   ├── e2e/                     Puppeteer screenshot tests
│   └── treeshake/               Tree-shaking verification
├── utils/
│   ├── build/                   Rollup build scripts, dev server
│   │   ├── rollup.config.js     Main build configuration
│   │   ├── dev.js               Development build script
│   │   └── preview.js           Preview build script
│   ├── docs/                    JSDoc config + template
│   ├── llms/                    LLM-friendly docs builder
│   │   └── build.js             LLM docs generation script
│   └── server.js                Development server
├── build/                       Built output (gitignored)
├── package.json                 Zero runtime deps, all devDependencies
├── eslint.config.js             ESLint flat config (ES2022)
└── LICENSE                      MIT

8.2 Class Hierarchy

three.js contains 411 classes. Below is the complete inheritance hierarchy for all major subsystems.

EventDispatcher Tree

EventDispatcher is the root mixin for nearly all stateful classes:

EventDispatcher
├── Object3D
│   ├── Scene
│   ├── Camera
│   │   ├── PerspectiveCamera
│   │   ├── OrthographicCamera
│   │   ├── ArrayCamera
│   │   ├── StereoCamera
│   │   └── CubeCamera
│   ├── Mesh
│   │   ├── InstancedMesh
│   │   ├── BatchedMesh
│   │   └── SkinnedMesh
│   ├── Line
│   │   ├── LineSegments
│   │   │   └── LineLoop (conceptual, same level)
│   │   └── LineLoop
│   ├── Points
│   ├── Sprite
│   ├── Group
│   │   └── ClippingGroup
│   ├── LOD
│   ├── Bone
│   ├── Light
│   │   ├── AmbientLight
│   │   ├── DirectionalLight
│   │   ├── PointLight
│   │   ├── SpotLight
│   │   ├── HemisphereLight
│   │   ├── RectAreaLight
│   │   └── LightProbe
│   ├── Audio
│   │   └── PositionalAudio
│   └── AudioListener
├── BufferGeometry
│   ├── BoxGeometry
│   ├── SphereGeometry
│   ├── PlaneGeometry
│   ├── CircleGeometry
│   ├── CylinderGeometry
│   ├── ConeGeometry
│   ├── TorusGeometry
│   ├── TorusKnotGeometry
│   ├── RingGeometry
│   ├── CapsuleGeometry
│   ├── DodecahedronGeometry
│   ├── IcosahedronGeometry
│   ├── OctahedronGeometry
│   ├── TetrahedronGeometry
│   ├── PolyhedronGeometry
│   ├── LatheGeometry
│   ├── ExtrudeGeometry
│   ├── ShapeGeometry
│   ├── TubeGeometry
│   ├── EdgesGeometry
│   └── WireframeGeometry
├── Material
│   ├── MeshBasicMaterial
│   ├── MeshLambertMaterial
│   ├── MeshPhongMaterial
│   ├── MeshStandardMaterial
│   │   └── MeshPhysicalMaterial
│   ├── MeshToonMaterial
│   ├── MeshNormalMaterial
│   ├── MeshDepthMaterial
│   ├── MeshDistanceMaterial
│   ├── MeshMatcapMaterial
│   ├── ShaderMaterial
│   │   └── RawShaderMaterial
│   ├── LineBasicMaterial
│   │   └── LineDashedMaterial
│   ├── PointsMaterial
│   ├── SpriteMaterial
│   └── ShadowMaterial
├── Texture
│   ├── CubeTexture
│   ├── CanvasTexture
│   ├── CompressedTexture
│   ├── CompressedArrayTexture
│   ├── Data3DTexture
│   ├── DataArrayTexture
│   ├── DataTexture
│   ├── DepthTexture
│   ├── FramebufferTexture
│   └── VideoTexture
├── Controls (src/extras/Controls.js)
│   └── [addon controls extend this: OrbitControls, etc.]
├── Node (src/nodes/core/Node.js)
│   ├── ArrayNode
│   ├── AssignNode
│   ├── AttributeNode
│   ├── BypassNode
│   ├── ConstNode
│   ├── ContextNode
│   ├── IndexNode
│   ├── InputNode
│   ├── IsolateNode
│   ├── MRTNode
│   ├── OutputStructNode
│   ├── ParameterNode
│   ├── PropertyNode
│   ├── StackNode
│   ├── StructNode
│   ├── TempNode
│   ├── UniformNode
│   ├── VarNode
│   ├── VaryingNode
│   └── [40+ more node types in nodes/utils/, nodes/materialx/]
└── Renderer (src/renderers/common/Renderer.js)
    └── [WebGPURenderer extends this]

Shadow Hierarchy

LightShadow
├── DirectionalLightShadow
├── SpotLightShadow
└── PointLightShadow

Math Classes (standalone, no inheritance)

Vector2, Vector3, Vector4
Matrix2, Matrix3, Matrix4
Quaternion, Euler
Color
Box2, Box3
Sphere, Ray, Plane, Line3, Triangle
Frustum, FrustumArray
Spherical, Cylindrical
SphericalHarmonics3
MathUtils (static utility object)

Interpolant Hierarchy

Interpolant
├── CubicInterpolant
├── LinearInterpolant
├── DiscreteInterpolant
├── QuaternionLinearInterpolant
└── BezierInterpolant

KeyframeTrack Hierarchy

KeyframeTrack
├── BooleanKeyframeTrack
├── ColorKeyframeTrack
├── NumberKeyframeTrack
├── QuaternionKeyframeTrack
├── StringKeyframeTrack
└── VectorKeyframeTrack

BufferAttribute Typed Variants

BufferAttribute
├── Int8BufferAttribute
├── Uint8BufferAttribute
├── Uint8ClampedBufferAttribute
├── Int16BufferAttribute
├── Uint16BufferAttribute
├── Int32BufferAttribute
├── Uint32BufferAttribute
├── Float16BufferAttribute
└── Float32BufferAttribute

Render Target Hierarchy

RenderTarget
├── WebGLRenderTarget
│   ├── WebGL3DRenderTarget
│   ├── WebGLArrayRenderTarget
│   └── WebGLCubeRenderTarget

Curve Hierarchy

Curve
├── CurvePath
│   └── Path
│       └── Shape
├── LineCurve
├── LineCurve3
├── QuadraticBezierCurve
├── QuadraticBezierCurve3
├── CubicBezierCurve
├── CubicBezierCurve3
├── SplineCurve
├── CatmullRomCurve3
├── EllipseCurve
│   └── ArcCurve

Loader Hierarchy

Loader
├── FileLoader
├── TextureLoader
├── ImageLoader
├── ObjectLoader
├── MaterialLoader
├── BufferGeometryLoader
├── AnimationLoader
├── AudioLoader
├── CubeTextureLoader
├── CompressedTextureLoader
├── DataTextureLoader
└── [addon loaders: GLTFLoader, OBJLoader, FBXLoader, etc.]

8.3 Rendering Pipeline

WebGLRenderer Pipeline

The legacy WebGLRenderer.render(scene, camera) executes the following sequence:

render(scene, camera)
│
├── 1. Update scene graph matrices
│   └── scene.updateMatrixWorld()
│       └── Recursively computes world matrices for all children
│
├── 2. Update camera matrices
│   ├── camera.updateMatrixWorld()
│   └── camera.matrixWorldInverse = inverse(camera.matrixWorld)
│
├── 3. Project objects (frustum culling + sorting)
│   ├── Traverse scene graph
│   ├── Test each object against camera frustum (if frustumCulled=true)
│   ├── Check object.layers vs camera.layers
│   ├── Sort opaque objects front-to-back (minimize overdraw)
│   └── Sort transparent objects back-to-front (correct blending)
│
├── 4. Render shadow maps (if shadowMap.enabled)
│   ├── For each shadow-casting light:
│   │   ├── Set up shadow camera
│   │   ├── Render scene from light's perspective
│   │   └── Store depth in shadow map texture
│   └── Uses MeshDepthMaterial/MeshDistanceMaterial internally
│
├── 5. Set render state
│   ├── Clear buffers (if autoClear)
│   ├── Set viewport, scissor
│   └── Bind render target (or default framebuffer)
│
├── 6. Render opaque objects
│   ├── For each object in opaque list:
│   │   ├── Select/compile shader program
│   │   ├── Set uniforms (matrices, lights, material props)
│   │   ├── Bind textures, attributes, index buffer
│   │   └── Issue draw call (drawArrays or drawElements)
│   └── Instanced: drawArraysInstanced / drawElementsInstanced
│
├── 7. Render transparent objects
│   └── Same as step 6, back-to-front order
│
└── 8. Post-render
    ├── Reset state
    ├── Update info (render count, triangle count, etc.)
    └── Call animation loop callback

WebGPURenderer Pipeline (Modern Path)

The WebGPU path shares the same high-level structure but uses a backend abstraction:

Renderer.render(scene, camera)           ← src/renderers/common/Renderer.js
│
├── Backend.beginRender(renderContext)    ← WebGPUBackend or WebGLBackend
├── RenderList.sort()                    ← Opaque + transparent lists
├── For each RenderObject:
│   ├── NodeBuilder.build()              ← Compile node graph to shader
│   ├── Backend.draw(renderObject)       ← Issue GPU draw call
│   └── RenderBundle (if applicable)     ← Reuse encoded commands
└── Backend.finishRender(renderContext)

The DataMap pattern is used extensively in the common renderer to associate internal GPU state (pipelines, bind groups, buffers) with user-facing objects without modifying them:

// Internal pattern used by renderer infrastructure
class SomeManager {
  constructor() {
    this._dataMap = new DataMap();
  }
  
  getInternalState(object) {
    return this._dataMap.get(object);
  }
}

Coordinate Systems

three.js uses a right-handed coordinate system (Y-up). The renderers handle coordinate system differences:

System Clip Space Z Constant
WebGL [-1, 1] WebGLCoordinateSystem
WebGPU [0, 1] WebGPUCoordinateSystem

Attempting to use CubeCamera or certain operations with the wrong coordinate system will throw: "THREE.CubeCamera: Invalid coordinate system." or "THREE.Frustum: Invalid coordinate system.".

8.4 Node System / TSL Architecture

The Three.js Shading Language (TSL) is a node-based shader authoring system that compiles to GLSL or WGSL depending on the renderer backend. It lives in src/nodes/.

Node Graph Architecture

User Code (TSL API)
       │
       ▼
  Node Graph (DAG of Node instances)
       │
       ▼
  NodeBuilder
       │
  ┌────┴────┐
  ▼         ▼
GLSL      WGSL
(WebGL)   (WebGPU)

Node (src/nodes/core/Node.js, extends EventDispatcher) is the base class. Every node represents a value or operation in the shader graph. Key properties:

  • nodeType — data type (float, vec2, vec3, vec4, mat3, mat4, etc.)
  • updateType — when to update (NONE, FRAME, RENDER, OBJECT)
  • build(builder) — compile this node for the target backend

Core Node Types (src/nodes/core/)

Node Class Purpose
ArrayNode Array data in shaders
AssignNode Variable assignment
AttributeNode Vertex attributes (position, normal, uv)
BypassNode Pass-through with side effects
ConstNode Constant values
ContextNode Execution context scoping
IndexNode Vertex/instance index
InputNode External input values
IsolateNode Prevent node sharing optimization
MRTNode Multiple Render Targets
OutputStructNode Structured shader output
ParameterNode Shader parameters
PropertyNode Material properties
StackNode Sequential operation stacking
StructNode Struct definitions
TempNode Temporary variables
UniformNode Uniform values
VarNode Variable declarations
VaryingNode Vertex-to-fragment interpolation

Utility Nodes (src/nodes/utils/)

Node Class Purpose
ConvertNode Type conversion
CubeMapNode Cube map sampling
FlipNode UV/coordinate flipping
JoinNode Combine components (e.g., vec2 → vec4)
LoopNode Shader loops
ReflectorNode Planar reflections
RotateNode Rotation transforms
RTTNode Render-to-texture
SplitNode Extract components (e.g., vec4.xyz)

NodeBuilder Compilation

NodeBuilder compiles the node graph into shader code:

  1. Analyze: Traverse the graph, determine types, resolve dependencies
  2. Build: Generate shader code for each node in dependency order
  3. Link: Produce final vertex/fragment shader with all declarations

Error conditions during build:

  • "THREE.NodeBuilder: Invalid chaining." — node graph contains invalid connections
  • Type mismatch errors when incompatible nodes are connected
  • Missing attribute errors when required geometry data is absent

TSL Helper Functions

src/Three.TSL.js re-exports hundreds of helper functions that provide a fluent API for constructing node graphs:

import { color, float, vec3, texture, normalMap, 
         mix, step, smoothstep, clamp } from 'three/tsl';

// Build a shader graph using TSL
const material = new NodeMaterial();
material.colorNode = mix(color(0xff0000), color(0x0000ff), uv().x);

MaterialX Integration

src/nodes/materialx/lib/ provides MaterialX standard surface node implementations, enabling import of MaterialX-defined materials into the TSL system.

8.5 Module Reference

Core Modules (src/core/)

Module Lines Key Exports Patterns
Object3D.js Large Object3D Scene graph node, parent/child, transform stack
BufferGeometry.js Large BufferGeometry Attribute container, bounding volumes
BufferAttribute.js Medium BufferAttribute + 9 typed variants Typed array wrapper, update ranges
EventDispatcher.js Small EventDispatcher Observer pattern, addEventListener/dispatch
Raycaster.js Medium Raycaster Mouse picking, recursive intersection
Clock.js Small Clock Delta time, elapsed time
Layers.js Small Layers 32-bit bitmask channel system
Uniform.js Small Uniform, UniformsGroup Shader uniform containers
RenderTarget.js Medium RenderTarget + WebGL variants Off-screen render destinations

Renderer Modules (src/renderers/)

Module Key Exports Notes
WebGLRenderer.js WebGLRenderer ~3000 lines, legacy path, direct WebGL state management
common/Renderer.js Renderer Base class for modern path (WebGPU/WebGL-fallback)
common/Backend.js Backend Abstract GPU backend interface
common/RenderList.js RenderList Opaque/transparent sorting
common/RenderObject.js RenderObject Per-object render state
common/RenderBundle.js RenderBundle Command recording for reuse
common/RenderContext.js RenderContext Render pass parameters
common/DataMap.js DataMap WeakMap-like internal state association
common/PostProcessing.js PostProcessing Built-in post-processing for modern path
webgpu/WebGPUBackend.js WebGPUBackend WebGPU native backend
webgl-fallback/WebGLBackend.js WebGLBackend WebGL backend for modern path

Animation Modules (src/animation/)

Module Key Exports Purpose
AnimationMixer.js AnimationMixer Drives all animations on a root object
AnimationClip.js AnimationClip Named collection of keyframe tracks
AnimationAction.js AnimationAction Playback control (play, stop, crossfade, loop)
KeyframeTrack.js KeyframeTrack + 6 typed variants Time→value interpolation
PropertyBinding.js PropertyBinding Resolves track name strings to object properties
AnimationObjectGroup.js AnimationObjectGroup Groups objects for shared animation
AnimationUtils.js AnimationUtils Static helpers (makeClipAdditive, subclip)

PropertyBinding handles the name resolution syntax: "objectName.propertyName[index]". Parse failures emit descriptive errors.

Loader Modules (src/loaders/)

Module Key Exports Purpose
Loader.js Loader Base class: path, crossOrigin, headers
LoadingManager.js LoadingManager, DefaultLoadingManager Coordination: onStart/onLoad/onProgress/onError
FileLoader.js FileLoader XHR-based file loading, responseType support
Cache.js Cache Global file-level caching (enabled/disabled)
TextureLoader.js TextureLoader Image → Texture
ImageLoader.js ImageLoader Image element loading
ObjectLoader.js ObjectLoader Full scene deserialization from JSON

Math Modules (src/math/)

Module Key Exports Notable Methods
Vector3.js Vector3 add, sub, multiply, dot, cross, normalize, length, lerp, applyMatrix4, applyQuaternion, project, unproject
Matrix4.js Matrix4 multiply, invert, decompose, compose, lookAt, makeRotation*, makeTranslation, makePerspective, makeOrthographic
Quaternion.js Quaternion setFromAxisAngle, setFromRotationMatrix, setFromEuler, slerp, multiply, invert
Euler.js Euler setFromRotationMatrix, setFromQuaternion, reorder; order: 'XYZ'/'YXZ'/etc.
Color.js Color set, setHex, setRGB, setHSL, lerp, multiply, convertSRGBToLinear, convertLinearToSRGB
Box3.js Box3 setFromObject, expandByPoint, containsPoint, intersectsBox, clampPoint
Sphere.js Sphere setFromPoints, containsPoint, intersectsBox, intersectsPlane
Ray.js Ray intersectBox, intersectSphere, intersectTriangle, intersectPlane
Frustum.js Frustum setFromProjectionMatrix, intersectsObject, containsPoint
MathUtils.js MathUtils clamp, degToRad, radToDeg, lerp, generateUUID, randFloat, randInt, isPowerOfTwo

Extras Modules (src/extras/)

Module Key Exports Purpose
Controls.js Controls Base class: object, domElement, enabled, connect/disconnect/dispose
curves/*.js Various Curve classes Parametric curves for paths and extrusions
core/Path.js Path, ShapePath 2D path operations: moveTo, lineTo, bezierCurveTo, arc
core/Shape.js Shape 2D shape with holes, for extrusion
PMREMGenerator.js PMREMGenerator Prefiltered environment maps for IBL
DataUtils.js DataUtils toHalfFloat/fromHalfFloat conversion
ImageUtils.js ImageUtils Image data manipulation
ShapeUtils.js ShapeUtils Triangulation: area, isClockWise, triangulateShape
TextureUtils.js TextureUtils Texture manipulation helpers
Earcut.js Earcut Polygon triangulation algorithm

8.6 Code Patterns & Conventions

Naming Conventions

Convention Example Usage
PascalCase MeshStandardMaterial Classes, constructors
camelCase updateMatrixWorld Methods, properties
UPPER_SNAKE FrontSide, PCFShadowMap Constants (in constants.js)
_ prefix _vector, _matrix Module-level reusable temp objects
is prefix isMesh, isBufferGeometry Type-check booleans

Type Checking Pattern

three.js uses boolean flags instead of instanceof for type checking throughout the codebase:

// Every class sets a type flag
class Mesh extends Object3D {
  constructor() {
    super();
    this.isMesh = true;
    this.type = 'Mesh';
  }
}

// Checked via property access (faster than instanceof, survives bundling)
if (object.isMesh) { /* ... */ }
if (material.isMeshStandardMaterial) { /* ... */ }

Reusable Temporary Objects

To avoid garbage collection pressure, modules declare module-scope temporary math objects:

const _vector = new Vector3();
const _matrix = new Matrix4();
const _quaternion = new Quaternion();

class SomeClass {
  someMethod() {
    // Reuse instead of allocating
    _vector.set(1, 2, 3).applyMatrix4(this.matrix);
  }
}

Caution

These temporaries are shared within a module. They are not thread-safe and should not be stored or passed to callbacks that may execute asynchronously.

Clone/Copy Pattern

Every data class implements both clone() and copy():

class Material extends EventDispatcher {
  copy(source) {
    this.name = source.name;
    this.color.copy(source.color);
    // ... copy all properties
    return this;
  }
  
  clone() {
    return new this.constructor().copy(this);
  }
}

Serialization Pattern

toJSON() produces a portable representation; the corresponding Loader reconstructs:

// Serialize
const json = object.toJSON();

// Deserialize
const loader = new THREE.ObjectLoader();
const reconstructed = loader.parse(json);

The JSON format uses metadata.type to identify the class and metadata.version for format evolution.

Dispose Pattern

GPU resources require explicit cleanup:

// Material tracks allocated resources
material.dispose();  // Fires 'dispose' event
// Listeners (e.g., renderer) clean up GPU resources in response

geometry.dispose();  // Fires 'dispose' event
texture.dispose();   // Fires 'dispose' event
renderTarget.dispose();
renderer.dispose();  // Cleans up everything

Console Warning Deduplication

src/utils.js provides warnOnce() functionality to prevent flooding the console with repeated warnings (e.g., deprecated API usage).

8.7 Testing Strategy

Test Organization

test/
├── unit/
│   ├── three.source.unit.js      Entry point for core tests
│   ├── three.addons.unit.js      Entry point for addon tests
│   └── src/                      Mirrors src/ directory structure
│       ├── core/
│       │   ├── Object3D.tests.js
│       │   ├── BufferGeometry.tests.js
│       │   └── ...
│       ├── math/
│       │   ├── Vector3.tests.js
│       │   └── ...
│       └── ...
├── e2e/
│   ├── puppeteer.js              E2E test runner
│   └── ...                       Reference screenshots
└── treeshake/
    └── ...                       Tree-shaking verification

Unit Test Framework

Tests use QUnit and follow a consistent structure:

QUnit.module('Core', () => {
  QUnit.module('Object3D', () => {
    QUnit.test('add/remove', (assert) => {
      const parent = new Object3D();
      const child = new Object3D();
      parent.add(child);
      assert.strictEqual(child.parent, parent);
    });
  });
});

E2E Screenshot Tests

The Puppeteer-based E2E system:

  1. Launches a headless browser
  2. Loads each example page
  3. Captures a screenshot after rendering stabilizes
  4. Compares pixel-by-pixel against reference images using pngjs/jpeg-js
  5. Fails if difference exceeds threshold
# Generate new reference screenshots
npm run make-screenshot

# Run comparison tests (WebGL)
npm run test-e2e

# Run comparison tests (WebGPU)
npm run test-e2e-webgpu

What Gets Tested

Level Coverage Runner
Unit Core classes, math, loaders, materials, geometries QUnit
E2E Visual output of examples (pixel comparison) Puppeteer
Tree-shaking Bundle size when importing individual classes Rollup
Lint Code style, JSDoc types ESLint

8.8 Editor Architecture

The three.js editor (editor/) is a full web-based 3D scene editor with an undo/redo system.

Command Pattern

All editor operations are encapsulated as command objects in editor/js/commands/:

Command Class Operation
AddObjectCommand Add object to scene
RemoveObjectCommand Remove object from scene
SetPositionCommand Change object position
SetRotationCommand Change object rotation
SetScaleCommand Change object scale
SetMaterialCommand Assign material to object
SetGeometryCommand Assign geometry to mesh
SetValueCommand Generic property change
SetColorCommand Change color property
SetMaterialValueCommand Change material property
SetMaterialColorCommand Change material color
SetMaterialMapCommand Change material texture
MoveObjectCommand Reparent in scene graph
MultiCmdsCommand Batch multiple commands
... 24 command classes total

Each command implements execute() and undo(), stored in a history stack.

UI System

The editor uses a custom UI framework (editor/js/libs/ui.js) with these widget classes:

UIElement, UIDiv, UIRow, UIPanel, UIInput, UISelect, UICheckbox, UIColor, UINumber, UIText, UITextArea, UIButton, UIBreak, UISpan

Script Editor

The editor includes a code editor (powered by CodeMirror) that allows users to write per-object scripts. Scripts are parsed with esprima for syntax validation.

XR Support

The editor viewport supports VR/XR preview modes for testing immersive experiences.

8.9 Potential Improvements

Based on source analysis and open issues:

Architecture

  1. WebGPU XR support: XR is not supported with the WebGPU backend — the error message "THREE.XRManager: XR is not supported with WebGPU backend." is explicit. This limits adoption of the modern renderer for immersive applications.

  2. Node system complexity: The TSL node system (src/nodes/) contains 40+ core node types. The NodeBuilder compilation path is complex and error-prone, with several known issues around type inference and chaining validation.

  3. WebGLRenderer size: At ~3,000 lines, WebGLRenderer.js is the largest single file. The modern path's Renderer.js + backend separation is a better architecture, but the legacy renderer cannot be refactored without breaking changes.

Known Open Issues

Issue Area Description
#33404 WebXR AR crashes Chrome 147+
#33394 TSL textureLoad with int(0) produces GLSL2 compile error
#33403 TSL Return() value support incomplete
#33291 Animation Reverse playback edge cases
#29963 GLTFLoader UTF-8 filename encoding
#31994 GLTFLoader Negative scaling affects lighting
#26613 SVGLoader Incorrect rendering of certain SVGs
#33416 TSL String/arrayBuffer method support
#33412 TSL setName on attributes

Code Quality Opportunities

  • Inconsistent error handling in BatchedMesh: Error messages reference % placeholder syntax (e.g., "%.reservedSpace exceeded") which suggests incomplete string formatting
  • Shadow map disposal: Shadow maps are not always cleaned up when lights are removed from the scene
  • Loader error propagation: Some addon loaders swallow errors silently rather than propagating to the LoadingManager.onError callback
  • DepthTexture format validation: The renderer validates DepthTexture formats but error messages could be more specific about which formats are supported for which operations

8.10 Contributing

Development Workflow

# Clone
git clone https://github.com/mrdoob/three.js.git
cd three.js

# Install dependencies
npm install

# Start development server
npm start
# → http://localhost:8080

# Run tests before submitting
npm test

# Lint and auto-fix
npm run lint-fix

# Build production bundles
npm run build

Code Style Requirements

  • ES2022 module syntax (import/export)
  • Single quotes for strings
  • const by default, let only when reassignment is needed
  • No var declarations
  • Tabs for indentation
  • No trailing semicolons in function declarations
  • All JSDoc types must be specified (@param {type}, @returns {type})
  • Throw Error objects, never literals (no-throw-literal: error)
  • Use spread syntax over .apply() (prefer-spread)
  • No duplicate imports from the same module (no-duplicate-imports)

File Organization Rules

  • One class per file (matching filename)
  • Constructor parameters first, then properties, then methods
  • clone() and copy() at the end of the class body
  • toJSON() after clone/copy
  • Static methods after instance methods

Addon Development

Addons live in examples/jsm/ and are published under three/addons:

// examples/jsm/controls/MyControl.js
import { Controls } from 'three';

class MyControl extends Controls {
  constructor(object, domElement) {
    super(object, domElement);
    // ...
  }
  
  connect() { /* bind events */ }
  disconnect() { /* unbind events */ }
  update() { /* per-frame logic */ }
  dispose() { /* cleanup */ }
}

export { MyControl };

Addons may import from three but never from three/src/* directly. This ensures they work with both source and built versions.

Tip

The Addons.js barrel file at examples/jsm/Addons.js re-exports all addons. When adding a new addon, add its export to this file so it is accessible via import { MyAddon } from 'three/addons'.


9. Integration Points

9.1 Addon Ecosystem

The examples/jsm/ directory contains the official addon ecosystem, published as three/addons. Major categories:

Controls

Addon Import Purpose
OrbitControls three/addons/controls/OrbitControls.js Orbit around target (most common)
MapControls three/addons/controls/MapControls.js Map-style pan/zoom/rotate
TrackballControls three/addons/controls/TrackballControls.js Unconstrained rotation
FlyControls three/addons/controls/FlyControls.js Flight simulator-style
FirstPersonControls three/addons/controls/FirstPersonControls.js FPS camera
PointerLockControls three/addons/controls/PointerLockControls.js Pointer lock FPS
ArcballControls three/addons/controls/ArcballControls.js Arcball rotation
DragControls three/addons/controls/DragControls.js Drag objects in 3D
TransformControls three/addons/controls/TransformControls.js Translate/rotate/scale gizmo

All controls extend Controls (src/extras/Controls.js) which provides: object, domElement, enabled, state, keys, mouseButtons, touches, and connect()/disconnect()/update()/dispose() lifecycle methods.

Loaders

Addon Format Notes
GLTFLoader glTF 2.0 (.gltf, .glb) Primary 3D format, supports extensions
DRACOLoader Draco compressed Used with GLTFLoader for compressed meshes
KTX2Loader KTX2/Basis Universal GPU-compressed textures
OBJLoader Wavefront OBJ Legacy format, no animation
FBXLoader Autodesk FBX Animation support
STLLoader Stereolithography 3D printing format
PLYLoader Stanford PLY Point clouds, meshes
PCDLoader Point Cloud Data Point clouds
ColladaLoader COLLADA (.dae) XML-based, older format
SVGLoader SVG 2D vector to 3D geometry
RGBELoader Radiance RGBE (.hdr) HDR environment maps
EXRLoader OpenEXR HDR images
HDRCubeTextureLoader HDR Cube HDR cube map textures
LDrawLoader LDraw LEGO brick models
3DMLoader Rhinoceros 3DM Rhino models
3MFLoader 3MF 3D manufacturing format
VRMLLoader VRML Legacy 3D web format
BVHLoader BVH Motion capture data

Note

GLTFLoader is the recommended format for web 3D. It supports PBR materials, animations, morph targets, and numerous extensions. Use DRACOLoader for mesh compression and KTX2Loader for texture compression to minimize download sizes.

Exporters

Addon Format
GLTFExporter glTF 2.0 (.gltf, .glb)
OBJExporter Wavefront OBJ
STLExporter STL (ASCII/binary)
PLYExporter Stanford PLY
USDZExporter Apple USDZ (AR Quick Look)
ColladaExporter COLLADA (.dae)

Post-Processing

import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new ShaderPass(someShader));

function animate() {
  composer.render();  // Instead of renderer.render()
}

Tip

The WebGPU path has built-in post-processing via PostProcessing (src/renderers/common/PostProcessing.js) that uses TSL nodes instead of the addon EffectComposer. For the modern path, prefer the built-in system.

Utilities

Addon Purpose
BufferGeometryUtils mergeGeometries(), computeMikkTSpaceTangents(), interleave attributes
SceneOptimizer Automated scene optimization
ShadowMapViewer Debug visualization for shadow maps
WorkerPool Web Worker thread pool for parallel loading
LDrawUtils LDraw model processing utilities

9.2 Build Tool Integration

Vite

// vite.config.js
export default {
  // No special configuration needed — three.js works out of the box
  // Optionally optimize dependency pre-bundling:
  optimizeDeps: {
    include: ['three']
  }
};

Webpack

// webpack.config.js
module.exports = {
  resolve: {
    // three.js uses package.json exports field (webpack 5+)
    // No additional configuration needed
  }
};

TypeScript

three.js ships with TypeScript type definitions generated from JSDoc annotations. No @types/three package is needed for recent versions:

import * as THREE from 'three';

const scene: THREE.Scene = new THREE.Scene();
const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);

9.3 Framework Integration

React (react-three-fiber)

The React ecosystem uses @react-three/fiber as the standard binding:

import { Canvas } from '@react-three/fiber';

function App() {
  return (
    <Canvas>
      <mesh>
        <boxGeometry />
        <meshStandardMaterial color="orange" />
      </mesh>
      <ambientLight />
      <directionalLight position={[10, 10, 5]} />
    </Canvas>
  );
}

Web Components

three.js scenes can be encapsulated in web components:

class ThreeScene extends HTMLElement {
  connectedCallback() {
    const renderer = new THREE.WebGLRenderer();
    this.appendChild(renderer.domElement);
    // ... setup scene
  }
  
  disconnectedCallback() {
    this.renderer.dispose();
    // ... cleanup
  }
}
customElements.define('three-scene', ThreeScene);

9.4 WebXR Integration

three.js includes built-in WebXR support through the renderer's XR manager:

renderer.xr.enabled = true;

// VR
document.body.appendChild(VRButton.createButton(renderer));

// AR
document.body.appendChild(ARButton.createButton(renderer));

renderer.setAnimationLoop(function () {
  renderer.render(scene, camera);
});

Warning

XR is only supported with WebGLRenderer or the WebGLBackend of WebGPURenderer. Using the native WebGPUBackend with XR will throw: "THREE.XRManager: XR is not supported with WebGPU backend." Issue #33404 also documents WebXR AR crashes in Chrome 147+.

9.5 Audio Integration

three.js wraps the Web Audio API for spatialized 3D audio:

const listener = new THREE.AudioListener();
camera.add(listener);

// Non-positional (background music)
const sound = new THREE.Audio(listener);
new THREE.AudioLoader().load('music.mp3', (buffer) => {
  sound.setBuffer(buffer);
  sound.play();
});

// Positional (3D spatialized)
const positionalSound = new THREE.PositionalAudio(listener);
mesh.add(positionalSound);  // Sound follows the mesh in 3D space

// Analysis
const analyser = new THREE.AudioAnalyser(sound, 256);
const frequencyData = analyser.getFrequencyData();

AudioListener attaches to the camera and creates/manages the AudioContext singleton. PositionalAudio extends Audio with a PannerNode for 3D spatialization.

9.6 Devtools Integration

The devtools/ directory contains a browser extension panel for inspecting three.js scenes at runtime. It uses DevToolsEventTarget as a bridge between the page and the extension panel.

Applications opt into devtools support via the __THREE_DEVTOOLS__ global:

if (typeof __THREE_DEVTOOLS__ !== 'undefined') {
  __THREE_DEVTOOLS__.dispatchEvent(
    new CustomEvent('observe', { detail: scene })
  );
}

9.7 Helpers for Development

three.js includes visual debugging helpers that render directly in the scene:

Helper Visualizes
AxesHelper RGB axes (X=red, Y=green, Z=blue)
GridHelper Ground plane grid
PolarGridHelper Polar coordinate grid
ArrowHelper Direction vector arrow
BoxHelper Object bounding box wireframe
Box3Helper Box3 wireframe
PlaneHelper Plane normal and extent
CameraHelper Camera frustum wireframe
DirectionalLightHelper Directional light direction
HemisphereLightHelper Hemisphere light orientation
PointLightHelper Point light position
SpotLightHelper Spotlight cone
SkeletonHelper Bone structure wireframe
// Add axes helper to any object
const axes = new THREE.AxesHelper(5);
scene.add(axes);

// Visualize a camera's frustum
const helper = new THREE.CameraHelper(camera);
scene.add(helper);

10. Troubleshooting

10.1 Common Errors and Solutions

Note

Below are the most frequently encountered errors from the 82 error messages in the three.js source. Each includes the exact message text and resolution steps.

WebGL Context Errors

Error: THREE.WebGLRenderer: WebGL 1 is not supported since r163. Cause: Browser or device only supports WebGL 1. Solution: Use a modern browser (Chrome 80+, Firefox 78+, Safari 14+) that supports WebGL 2.

Error: WebGL context creation failures Cause: GPU drivers, browser extensions blocking WebGL, or too many active contexts. Solution: Check navigator.gpu or canvas.getContext('webgl2') before creating a renderer. Dispose unused renderers.

Renderer Initialization

Error: Methods called before renderer.init() completes Cause: WebGPURenderer requires async initialization. Solution: Always await renderer.init() before calling render(), setSize(), or other methods.

Buffer and Attribute Errors

Warning

BufferAttribute validates that its data is a typed array. Passing a regular Array or incorrect type will throw an error. Always use Float32Array, Uint16Array, etc.

Error: BufferAttribute typed array check Cause: Non-typed array passed to BufferAttribute constructor. Solution: Use the appropriate typed variant (Float32BufferAttribute, etc.) or pass a typed array.

Depth Texture Errors

Error: WebGLRenderTarget DepthTexture size mismatch Cause: DepthTexture dimensions don't match the RenderTarget. Solution: Ensure DepthTexture width/height matches the RenderTarget's width/height.

Error: DepthTexture format validation Cause: Invalid format/type combination for DepthTexture. Solution: Use DepthFormat with UnsignedIntType or DepthStencilFormat with UnsignedInt248Type.

BatchedMesh Errors

Error: THREE.BatchedMesh: Maximum item count reached. Cause: Attempted to add more instances than maxInstanceCount. Solution: Increase maxInstanceCount in the constructor, or reuse instances.

Error: THREE.BatchedMesh: %.reservedSpace exceeded. Cause: Geometry vertex/index data exceeds reserved space. Solution: Increase maxVertexCount/maxIndexCount in the constructor.

Animation Errors

Error: KeyframeTrack validation errors Cause: Invalid keyframe data (NaN values, non-monotonic times, mismatched array lengths). Solution: Validate animation data before creating tracks. Use AnimationClip.validate().

Error: PropertyBinding parse failures Cause: Invalid binding path string format. Solution: Use the format "objectName.propertyName[index]". Verify object names match scene hierarchy.

Node System Errors

Error: THREE.NodeBuilder: Invalid chaining. Cause: Incompatible node connections in TSL graph. Solution: Check that connected nodes have compatible types. Use explicit type conversion nodes.

Multiple Instances Warning

Warning: Multiple Three.js instances detected Cause: Application loads more than one copy of three.js. Solution: Check for duplicate dependencies. Use import maps or bundler aliases to ensure a single copy. Run npm ls three to find duplicates.

Coordinate System Errors

Error: THREE.CubeCamera: Invalid coordinate system. Cause: CubeCamera used with wrong coordinate system setting. Solution: Ensure the renderer's coordinate system matches expectations. WebGL uses WebGLCoordinateSystem, WebGPU uses WebGPUCoordinateSystem.

Tip

Enable the three.js devtools browser extension for real-time scene inspection. Many rendering issues (missing objects, incorrect materials, wrong transforms) are immediately visible in the scene graph inspector.

10.2 Performance Debugging

Symptom Likely Cause Diagnostic
Low FPS, high draw calls Too many objects renderer.info.render.calls
Low FPS, few draw calls Complex shaders/geometry GPU profiler (Chrome DevTools → Performance)
Memory growth over time Missing dispose() calls renderer.info.memory (geometries, textures)
Stuttering Garbage collection Avoid allocations in render loop
Black screen Missing lights or wrong material Add AmbientLight, check material.side
Objects invisible Wrong layer, frustum culled, scale 0 Check object.layers, object.visible, object.scale
// Monitor renderer statistics
console.log(renderer.info.render.calls);       // Draw calls per frame
console.log(renderer.info.render.triangles);   // Triangles per frame
console.log(renderer.info.memory.geometries);  // Allocated geometries
console.log(renderer.info.memory.textures);    // Allocated textures