
Threejs
- 19 installs
- 7 repo stars
- Updated June 18, 2026
- duc01226/easyplatform
Helps build 3D web apps with Three.js (WebGL/WebGPU) using 556 searchable examples, 60 API classes and 20 use cases - scenes, GLTF models, animation, physics, VR/XR.
About
threejs is a skill for building 3D web applications with Three.js across WebGL and WebGPU, backed by 556 searchable examples, 60 API classes and 20 use cases covering scenes, model loading, animation, physics, post-processing and VR/XR. A solo builder reaches for it when adding interactive 3D graphics to a web app and wants concrete example-driven guidance instead of reading raw Three.js docs.
- 556 searchable Three.js examples
- 60 API classes, 20 use cases
- GLTF loading, PBR, particles, shadows
- WebGPU, physics, spatial audio, VR/XR
Threejs by the numbers
- 19 all-time installs (skills.sh)
- Ranked #1,568 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/duc01226/easyplatform --skill threejsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 19 |
|---|---|
| repo stars | ★ 7 |
| Last updated | June 18, 2026 |
| Repository | duc01226/easyplatform ↗ |
What it does
Helps build 3D web apps with Three.js (WebGL/WebGPU) using 556 searchable examples, 60 API classes and 20 use cases - scenes, GLTF models, animation, physics, VR/XR.
Who is it for?
adding interactive 3D to web apps
Skip if: 2D-only or non-graphics work
Files
Codex compatibility note:
>
- Invoke repository skills with$skill-namein Codex; this mirrored copy rewrites legacy Claude/skill-namereferences.
- Task tracker mandate: BEFORE executing any workflow or skill step, create/update task tracking for all steps and keep it synchronized as progress changes.
- User-question prompts mean to ask the user directly in Codex.
- Ignore Claude-specific mode-switch instructions when they appear.
- Strict execution contract: when a user explicitly invokes a skill, execute that skill protocol as written.
- Subagent authorization: when a skill is user-invoked or AI-detected and its protocol requires subagents, that skill activation authorizes use of the required spawn_agent subagent(s) for that task.- Do not skip, reorder, or merge protocol steps unless the user explicitly approves the deviation first.
- For workflow skills, execute each listed child-skill step explicitly and report step-by-step evidence.
- If a required step/tool cannot run in this environment, stop and ask the user before adapting.
<!-- CODEX:PROJECT-REFERENCE-LOADING:START -->
Codex Project-Reference Loading (No Hooks)
Codex uses static project-reference loading instead of runtime-injected project docs. When coding, planning, debugging, testing, or reviewing, open project docs explicitly using this routing.
Always read:
docs/project-config.json(project-specific paths, commands, modules, and workflow/test settings)docs/project-reference/docs-index-reference.md(routes to the fulldocs/project-reference/*catalog)docs/project-reference/lessons.md(always-on guardrails and anti-patterns)
Missing/stale context route: If docs/project-config.json, the docs index, lessons.md, CLAUDE.md, AGENTS.md, or any task-required reference doc is missing or stale, auto-run $project-init or the narrow setup route ($project-config, $docs-init, $scan-all, $scan --target=<key>, $claude-md-init) before ordinary project-specific work. If Codex mirrors or AGENTS.md are missing/stale, ask the user to run $sync-codex; do not auto-run it.
Situation-based docs:
- Backend/CQRS/API/domain/entity changes:
backend-patterns-reference.md,domain-entities-reference.md,project-structure-reference.md - Frontend/UI/styling/design-system:
frontend-patterns-reference.md,scss-styling-guide.md,design-system/README.md - Spec authoring,
docs/specs/pathing, or TC format:feature-spec-reference.md,spec-system-reference.md,spec-principles.md - Behavior/public-contract changes or spec-test-code sync:
workflow-spec-test-code-cycle-reference.mdplus the spec docs above - Derived spec indexes/ERDs/reimplementation guides:
spec-system-reference.mdand source Feature Specs underdocs/specs/ - Integration test implementation/review:
integration-test-reference.md - E2E test implementation/review:
e2e-test-reference.md - Code review/audit work:
code-review-rules.mdplus domain docs above based on changed files
Do not read all docs blindly. Start from docs-index-reference.md, then open only relevant files for the task.
<!-- CODEX:PROJECT-REFERENCE-LOADING:END -->
Quick Summary
Goal: Build 3D web apps with Three.js (WebGL/WebGPU). 556 searchable examples, 60 API classes, 20 use cases. Actions: create 3D scene, load model, add animation, implement physics, build VR/XR. Topics: GLTF loader, PBR materials, particle effects, shadows, post-processing, compute shaders, TSL. Integrations: WebGPU, physics engines, spatial audio.
Workflow:
1. Detect — classify request scope and target artifacts. 2. Execute — apply required steps with evidence-backed actions. 3. Verify — confirm constraints, output quality, and completion evidence.
Key Rules:
- MUST ATTENTION keep claims evidence-based (
file:line) with confidence >80% to act. - MUST ATTENTION keep task tracking updated as each step starts/completes.
- NEVER skip mandatory workflow or skill gates.
When to Use
- Building 3D scenes, games, or visualizations
- Loading 3D models (GLTF, FBX, OBJ)
- Implementing animations, physics, or VR/XR
- Creating particle effects or custom shaders
- Optimizing rendering performance
Search Examples & API
Use the search CLI to find relevant examples and API references:
python3 .claude/skills/threejs/scripts/search.py "<query>" [--domain <domain>] [-n <max_results>]Search Domains
| Domain | Use For | Example Query |
|---|---|---|
examples | Find code examples | "particle effects gpu" |
api | Class/method reference | "PerspectiveCamera" |
use-cases | Project recommendations | "product configurator" |
categories | Browse categories | "webgpu" |
Quick Examples
# Find particle/compute examples
python3 .claude/skills/threejs/scripts/search.py "particle compute webgpu"
# Search API for camera classes
python3 .claude/skills/threejs/scripts/search.py "camera" --domain api
# Get examples for a use case
python3 .claude/skills/threejs/scripts/search.py "product configurator" --use-case
# Filter by category
python3 .claude/skills/threejs/scripts/search.py --category webgpu -n 10
# Filter by complexity
python3 .claude/skills/threejs/scripts/search.py --complexity high -n 5Example Categories
| Category | Count | Description |
|---|---|---|
webgl | 216 | Standard WebGL rendering |
webgpu (wip) | 190 | Modern WebGPU + compute shaders |
webgl / advanced | 48 | Low-level GPU, custom shaders |
webgl / postprocessing | 27 | Bloom, SSAO, SSR, DOF |
webxr | 26 | VR/AR experiences |
physics | 13 | Physics simulation |
Common Use Cases
| Use Case | Recommended | Complexity |
|---|---|---|
| Product Configurator | GLTF, PBR, EnvMaps | Medium |
| Game Development | Animation, Physics, Controls | High |
| Data Visualization | BufferGeometry, Points | Medium |
| 360 Panorama | Equirectangular, WebXR | Low |
| Architectural Viz | GLTF, HDR, CSM Shadows | High |
Quick Start
// 1. Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// 2. Lighting
scene.add(new THREE.AmbientLight(0x404040));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);
// 3. Load GLTF Model
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('model.glb', gltf => scene.add(gltf.scene));
// 4. Animation Loop
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();Progressive Reference Files
Level 1: Fundamentals
references/00-fundamentals.md- Core concepts, scene graphreferences/01-getting-started.md- Setup, basic rendering
Level 2: Common Tasks
references/02-loaders.md- GLTF, FBX, OBJ loadersreferences/03-textures.md- Texture types, mappingreferences/04-cameras.md- Camera types, controlsreferences/05-lights.md- Light types, shadowsreferences/06-animations.md- AnimationMixer, clipsreferences/11-materials.md- PBR, standard materialsreferences/18-geometry.md- BufferGeometry, primitives
Level 3: Interactive
references/08-interaction.md- Raycasting, pickingreferences/09-postprocessing.md- Bloom, SSAO, SSRreferences/10-controls.md- OrbitControls, etc.
Level 4: Advanced
references/12-performance.md- Instancing, LOD, batchingreferences/13-node-materials.md- TSL shader graphsreferences/17-shader.md- Custom GLSL shaders
Level 5: Specialized
references/14-physics-vr.md- Physics, WebXRreferences/16-webgpu.md- WebGPU, compute shaders
External Resources
- Docs: https://threejs.org/docs/
- Examples: https://threejs.org/examples/
- Editor: https://threejs.org/editor/
- Discord: https://discord.gg/56GBJwAnUS
---
Three.js Development
Build high-performance 3D web applications using Three.js. Contains 556 searchable examples across 13 categories, 60 API classes, and 20 use-case templates.
<!-- SYNC:ai-mistake-prevention -->
AI Mistake Prevention — Failure modes to avoid on every task:
>
Re-read files after context changes. Context compaction, resume, or long-running work can make memory stale; verify current files before acting.
Verify generated content against source evidence. AI hallucinates APIs, names, claims, and document facts. Check the relevant source before documenting or referencing.
Check downstream references before deleting or renaming. Removing an artifact can stale docs, generated mirrors, configs, and callers; map references first.
Trace the full impact chain after edits. Changing a definition can miss derived outputs and consumers. Follow the affected chain before declaring done.
Verify ALL affected outputs, not just the first. One green check is not all green checks; validate every output surface the change can affect.
Assume existing values are intentional — ask WHY before changing. Before changing a constant, limit, flag, wording, or pattern, read nearby context and history.
Surface ambiguity before acting — don't pick silently. Multiple valid interpretations require an explicit question or stated assumption with risk.
Keep shared guidance role-relevant. Universal guidance must help every receiving skill or agent; code-specific obligations belong only in code-specific protocols.
<!-- /SYNC:ai-mistake-prevention -->
<!-- SYNC:critical-thinking-mindset -->
Critical Thinking Mindset — Apply critical thinking, sequential thinking. Every claim needs traced proof, confidence >80% to act.
Anti-hallucination: Never present guess as fact — cite sources for every claim, admit uncertainty freely, self-check output for errors, cross-reference independently, stay skeptical of own confidence — certainty without evidence root of all hallucination.
<!-- /SYNC:critical-thinking-mindset -->
<!-- SYNC:critical-thinking-mindset:reminder -->
MUST ATTENTION apply critical + sequential thinking — every claim needs appropriate traced evidence (file:line for repo/code claims; source URL or artifact section for research, product, content, and docs claims); confidence >80% to act, <60% DO NOT recommend. Anti-hallucination: never present guess as fact, admit uncertainty freely, cross-reference independently, stay skeptical of own confidence.
<!-- /SYNC:critical-thinking-mindset:reminder -->
<!-- SYNC:ai-mistake-prevention:reminder -->
MUST ATTENTION apply AI mistake prevention — verify generated content against evidence, trace downstream references before deleting or renaming, verify all affected outputs, re-read files after context loss, and surface ambiguity before acting.
<!-- /SYNC:ai-mistake-prevention:reminder -->
Closing Reminders
IMPORTANT MUST ATTENTION Protocols in force (concise digest of the SYNC/shared blocks this skill carries):
- AI Mistake Prevention: verify generated content against evidence, trace downstream references, verify all affected outputs, re-read after context loss, surface ambiguity.
- Critical Thinking: MUST ATTENTION traced
file:lineproof per claim; confidence >80% to act, NEVER guess.
IMPORTANT MUST ATTENTION break work into small todo tasks using task tracking BEFORE starting IMPORTANT MUST ATTENTION search codebase for 3+ similar patterns before creating new code IMPORTANT MUST ATTENTION cite file:line evidence for every claim (confidence >80% to act) IMPORTANT MUST ATTENTION add a final review todo task to verify work quality
[TASK-PLANNING] Before acting, analyze task scope and systematically break it into small todo tasks and sub-tasks using task tracking.
<!-- CODEX:SYNC-PROMPT-PROTOCOLS:START -->
Hookless Prompt Protocol Mirror (Auto-Synced)
Source: .claude/.ck.json + .claude/skills/shared/sync-inline-versions.md (:full blocks) + .claude/scripts/lib/hookless-prompt-protocol.cjs
[WORKFLOW-EXECUTION-PROTOCOL] [BLOCKING] Workflow Execution Protocol — MANDATORY IMPORTANT MUST CRITICAL. Do not skip for any reason.
Generic portability boundary: Reusable skills and protocol text stay project-neutral; project-specific conventions are discovered from docs/project-config.json and docs/project-reference/. Apply shared AI-SDD from shared/sdd-artifact-contract.md. Read docs/project-config.json and docs/project-reference/docs-index-reference.md, then open the project reference docs named there. For spec, test-case, behavior-change, public-contract, or docs/specs/ work, route through the local spec docs named by the docs index: feature-spec-reference.md, spec-system-reference.md, spec-principles.md, and workflow-spec-test-code-cycle-reference.md when specs/tests/code must stay synchronized. If either file or a required reference doc is missing or stale, auto-run $project-init (or the narrow lower-level route such as $project-config, $docs-init, $scan-all, or $scan --target=<key>) before ordinary project-specific work. Any supported AI tool may execute when this shared context and local docs are available.
1. DETECT: If the prompt starts with an explicit slash skill/workflow command, execute it directly. Otherwise match the prompt against the workflow catalog and skill list. 2. ANALYZE: Choose the best option: execute directly, invoke a skill, activate a standard workflow, or compose a custom step combination. 3. AUTO-SELECT: Pick the best option yourself. Do not ask the user to choose between direct execution, skill, standard workflow, or custom workflow. 4. ACTIVATE: For a selected workflow, call $start-workflow <workflowId>; for a selected skill, invoke that skill; for a custom workflow, sequence custom steps directly; for direct execution, proceed with the task. 5. CREATE TASKS: task tracking for ALL workflow/skill/custom steps before execution when the selected path has multiple steps. 6. EXECUTE: Advance per the Workflow Step Advancement & Parallel Phases rule in your context instructions — model-driven; a sub-agent completion advances a step identically to an inline call; a parallel-phase group is an all-return barrier (advance only after ALL members return, never serialize it)
Shared AI-SDD Protocol Markers
Source: .claude/skills/shared/sync-inline-versions.md
SYNC:ai-sdd-artifact-contract
AI-SDD Artifact Contract — Shared spec-driven development rules stay portable and source-owned.
>
1. Keep reusable AI-SDD principles in .claude; put repository-specific paths, commands, owners, products, and formats in project config/reference docs.2. Preserve cycle: spec -> plan -> tasks -> implement -> verify -> update spec/docs.3. Trace every requirement or invariant through decision, task, TC/test, source evidence, and docs/spec update.
4. Treat code-to-spec extraction as reference-only until accepted by the canonical spec owner.
5. Any supported AI tool may plan, implement, review, or verify with synced context; using multiple tools is optional.
6. Update.claudesource first, then sync generated mirrors; do not manually edit.agents,.codex, orAGENTS.md. — why: mirrors are generated artifacts; hand-edits are overwritten on the next sync
7. Ifdocs/project-config.json, root instruction files, or a required project-reference doc is missing or stale, auto-run$project-initor the narrow lower-level route before ordinary project-specific work.
>
Active reference: shared/sdd-artifact-contract.md in the active skills root.---
SYNC:ai-sdd-artifact-contract:reminder
- MANDATORY Apply
shared/sdd-artifact-contract.md; keep reusable AI-SDD in.claudeand local rules in project docs. - MANDATORY Code-to-spec extraction is reference-only until canonical acceptance; any supported AI tool may execute with synced context.
- MANDATORY Update
.claudesource before syncing generated mirrors; do not manually edit.agents,.codex, orAGENTS.md. - MANDATORY Missing or stale project config, root instruction files, or required reference docs route project-specific work through
$project-initor the narrow setup route automatically.
[TASK-PLANNING] [MANDATORY] BEFORE executing any workflow or skill step, create/update task tracking for all planned steps, then keep it synchronized as each step starts/completes.
[LESSON-LEARNED-REMINDER] [BLOCKING] Task Planning & Continuous Improvement — MANDATORY. Do not skip.
Break work into small tasks (task tracking) before starting. Add final task: "Analyze AI mistakes & lessons learned".
Extract lessons — ROOT CAUSE ONLY, not symptom fixes:
1. Name the FAILURE MODE (reasoning/assumption failure), not symptom — "assumed API existed without reading source" not "used wrong enum value". 2. Generality test: does this failure mode apply to ≥3 contexts/codebases? If not, abstract one level up. 3. Write as a universal rule — strip project-specific names/paths/classes. Useful on any codebase. 4. Consolidate: multiple mistakes sharing one failure mode → ONE lesson. 5. Recurrence gate: "Would this recur in future session WITHOUT this reminder?" — No → skip $learn. 6. Auto-fix gate: "Could $code-review/$code-simplifier/$security-review/$lint catch this?" — Yes → improve review skill instead. 7. BOTH gates pass → ask user to run $learn. [CRITICAL-THINKING-MINDSET] Apply critical thinking, sequential thinking. Every claim needs traced proof, confidence >80% to act. Anti-hallucination principle: Never present guess as fact — cite sources for every claim, admit uncertainty freely, self-check output for errors, cross-reference independently, stay skeptical of own confidence — certainty without evidence root of all hallucination. AI Attention principle (Primacy-Recency): Put the 3 most critical rules at both top and bottom of long prompts/protocols so instruction adherence survives long context windows. Goal-driven execution: Define success criteria first, loop until verified, and stop only when observable checks pass. Tests verify intent: Tests must protect business rules/invariants and fail when the protected intent breaks, not only mirror current behavior.
Common AI Mistake Prevention (System Lessons)
- Re-read files after context compaction. Edit requires prior Read in same context; compaction wipes read state. Re-read before editing.
- Grep for old terms after bulk replacements. AI over-trusts find/replace completeness. Grep full repo after bulk edits for missed refs in docs/configs/catalogs.
- Check downstream references before deleting. Deletions cascade doc/code staleness. Map referencing files before removal.
- After memory loss, check existing state before creating new. Compaction wipes prior-work memory. Query current state to resume — never blindly duplicate.
- Verify AI-generated content against actual code. AI hallucinates APIs, class names, method signatures. Grep to confirm existence before documenting/referencing.
- Trace full dependency chain after edits. Changing a definition misses downstream consumers. Trace the full chain.
- When renaming, grep ALL consumer file types. Some file types silently ignore missing refs (no compile error). Search code, templates, configs, generated files.
- Trace ALL code paths when verifying correctness. Code existing ≠ code executing. Trace early exits, error branches, conditional skips — not just happy path.
- Update docs that embed canonical data when source changes. Docs inlining derived data (workflows, schemas, configs) go stale silently. Update all embedding docs alongside source.
- Verify sub-agent results after context recovery. Background agents may finish while parent compacted — grep-verify output, don't trust assumed completion.
- Cross-check full target list against sub-agent assignments. Parallel sub-agents by category miss boundary items. Reconcile union of assignments against target list before proceeding.
- Sub-agents inherit knowledge only from their agent .md definition — use custom agent types, not built-in Explore. Tool adoption = permission + knowledge + enforcement (numbered workflow step).
- Persist sub-agent findings incrementally, not as a final batch. Long sub-agents hit cutoffs before final write — findings lost. Instruct append-per-section to report file.
- When debugging, ask "whose responsibility?" before fixing. Trace caller (wrong data) vs callee (wrong handling). Fix at responsible layer — never patch symptom site.
- Grep ALL removed names after extraction/refactoring. Primary file "done" ≠ secondary files clean. Grep entire scope for every removed symbol before declaring complete.
- Assume existing values are intentional — ask WHY before changing. Pattern-matching as "wrong" skips context. Before changing any constant/limit/flag: read comments, git blame, surrounding code.
- Verify ALL affected outputs, not just the first. One build green ≠ all green. Multi-stack changes (backend/frontend/tests/docs) require verifying EVERY output.
- Evaluate fit before copying a nearby pattern. Closest example ≠ matching preconditions — verify the new context shares the same constraints, base classes, scope, lifetime.
- Holistic-first debugging — resist nearest-attention trap. Don't dive into first plausible cause. List EVERY precondition (config, env vars, paths, DB, endpoints, creds, versions, DI, data). Verify each against evidence (grep/query — not reasoning). Ask "what would falsify this?" — if nothing, it's not a hypothesis. Most expensive failure: going deeper in "obvious" layer while bug sits in layer never questioned.
- Surgical changes — apply the diff test (context-aware). Two modes: (1) Bug fix → every line traces to the bug; no restyling; orphan cleanup only for imports YOUR changes made unused. (2) Review/enhancement → implement improvements AND announce as "Enhancement beyond main request: [what]". Never silently scope-creep. Diff test: "Would this line exist if I wasn't asked to do X?" — if no, delete or announce.
- Surface ambiguity before coding — don't pick silently. Multiple valid interpretations → present each with effort: "[Request] could mean (1) [N h], (2) [N h]. Which matters?" List scope/format/volume/constraints assumptions first. If simpler path exists, say so. Never silently pick.
- [MANDATORY FIRST ACTION] ALWAYS activate a suitable skill or workflow BEFORE responding. Match task against workflow catalog + skill list; invoke via skill invocation or
$start-workflow <workflowId>. NEVER answer or write code before checking. Skip = protocol violation. - Why-Review adversarial mindset — apply when reviewing any plan, decision, or design. Default SKEPTIC not VALIDATOR: steel-man a rejected alternative, invert each stated reason ("what does it sacrifice?"), stress-test top 2-3 assumptions, run pre-mortem ("ships, fails in 3 months — what breaks?"), surface 1-2 alternatives author missed. Section presence ≠ quality; quality = causal reasoning + concrete mitigations + evidence, not "it's better" or "monitor closely".
- Front-load report-write in sub-agent prompts for large reviews. Many-file sub-agents hit budget before final write — findings lost. Design prompts so: (1) report-write is first explicit deliverable, (2) append per-file/section (not batched), (3) scope bounded so reads don't exhaust budget. Truncated mid-sentence with no report file → spawn narrower scope, don't retry same prompt.
- After context compaction, re-verify all prior phase outcomes before continuing. Summaries describe intent, not environment state (git index, filesystem, processes). On resume, FIRST audit: git status, re-read modified files, verify filesystem. Every "completed" claim is an untested hypothesis until evidence confirms.
- OOM/memory: check row count before row size. Triage: (1) Unbounded query — no DB filter for trigger? Push filter to DB; eliminates OOM. (2) Large rows? Projection reduces proportionally. Row reduction > projection in ROI.
- Keep domain concepts out of generic/shared/infrastructure layers. Reusable layer (shared library, framework, infra module) must reference NO consumer-specific domain concept — tenant/customer/product IDs, business entities, feature rules. Leak compiles + runs → passes review silently while coupling the "reusable" layer to one consumer. Keep shared type domain-free; push domain fields/logic down into the consumer via subclass/composition. — why: a layer coupled to one consumer's domain is no longer reusable.
<!-- CODEX:SYNC-PROMPT-PROTOCOLS:END -->
ID,Category,Class,Keywords,Description,Common Methods,Related Classes
1,Core,Scene,"scene, container, root, hierarchy",Root container for all 3D objects,"add(), remove(), traverse(), getObjectByName()",Object3D; Group
2,Core,Camera,"camera, view, projection",Base camera class for all camera types,"lookAt(), updateProjectionMatrix()",PerspectiveCamera; OrthographicCamera
3,Core,PerspectiveCamera,"perspective, fov, aspect, near, far",Perspective projection camera,"setFocalLength(), updateProjectionMatrix()",Camera; OrthographicCamera
4,Core,OrthographicCamera,"orthographic, 2d, isometric, parallel",Orthographic projection camera,"updateProjectionMatrix(), zoom",Camera; PerspectiveCamera
5,Core,WebGLRenderer,"renderer, webgl, canvas, draw",WebGL rendering engine,"render(), setSize(), setPixelRatio(), dispose()",Scene; Camera
6,Core,WebGPURenderer,"renderer, webgpu, modern, compute",WebGPU rendering engine,"render(), setSize(), compute()",WebGLRenderer; Scene
7,Geometry,BufferGeometry,"geometry, vertices, faces, mesh",Efficient geometry representation,"setAttribute(), setIndex(), computeVertexNormals()",Mesh; BufferAttribute
8,Geometry,BoxGeometry,"box, cube, rectangular",Box/cube primitive geometry,"parameters: width, height, depth, segments",BufferGeometry; SphereGeometry
9,Geometry,SphereGeometry,"sphere, ball, globe",Sphere primitive geometry,"parameters: radius, widthSegments, heightSegments",BufferGeometry; BoxGeometry
10,Geometry,PlaneGeometry,"plane, flat, ground, floor",Flat plane primitive geometry,"parameters: width, height, widthSegments",BufferGeometry; CircleGeometry
11,Geometry,CylinderGeometry,"cylinder, tube, pillar",Cylinder primitive geometry,"parameters: radiusTop, radiusBottom, height",BufferGeometry; ConeGeometry
12,Geometry,TorusGeometry,"torus, donut, ring",Torus/donut primitive geometry,"parameters: radius, tube, radialSegments",BufferGeometry; TorusKnotGeometry
13,Material,Material,"material, appearance, surface",Base material class,"dispose(), clone(), needsUpdate",MeshStandardMaterial; ShaderMaterial
14,Material,MeshStandardMaterial,"standard, pbr, metalness, roughness",PBR metallic-roughness material,"color, metalness, roughness, map, normalMap",Material; MeshPhysicalMaterial
15,Material,MeshPhysicalMaterial,"physical, transmission, clearcoat, sheen",Advanced PBR material,"transmission, thickness, clearcoat, sheen, ior",MeshStandardMaterial; Material
16,Material,MeshBasicMaterial,"basic, unlit, simple, fast",Unlit material for simple rendering,"color, map, wireframe, transparent",Material; MeshLambertMaterial
17,Material,MeshLambertMaterial,"lambert, diffuse, non-shiny",Diffuse-only material,"color, map, emissive, reflectivity",Material; MeshPhongMaterial
18,Material,MeshPhongMaterial,"phong, specular, shiny, highlight",Specular highlight material,"color, specular, shininess, map",Material; MeshStandardMaterial
19,Material,MeshToonMaterial,"toon, cartoon, cel, stylized",Cel-shaded toon material,"color, gradientMap, map",Material; MeshLambertMaterial
20,Material,ShaderMaterial,"shader, custom, glsl, vertex, fragment",Custom GLSL shader material,"vertexShader, fragmentShader, uniforms",Material; RawShaderMaterial
21,Material,PointsMaterial,"points, particles, sprites",Material for point clouds,"color, size, sizeAttenuation, map",Material; SpriteMaterial
22,Light,Light,"light, illumination, source",Base light class,"color, intensity",DirectionalLight; PointLight
23,Light,DirectionalLight,"directional, sun, parallel, shadow",Parallel light rays (sun-like),"position, target, shadow, castShadow",Light; SpotLight
24,Light,PointLight,"point, bulb, omnidirectional",Omnidirectional light source,"position, distance, decay, shadow",Light; SpotLight
25,Light,SpotLight,"spotlight, cone, focused, shadow",Cone-shaped spotlight,"position, target, angle, penumbra, shadow",Light; DirectionalLight
26,Light,AmbientLight,"ambient, fill, global",Global ambient illumination,"color, intensity",Light; HemisphereLight
27,Light,HemisphereLight,"hemisphere, sky, ground, outdoor",Sky/ground gradient light,"skyColor, groundColor, intensity",Light; AmbientLight
28,Light,RectAreaLight,"rectarea, area, soft, studio",Rectangular area light,"width, height, intensity, lookAt()",Light; SpotLight
29,Object,Mesh,"mesh, geometry, material, object",3D object with geometry and material,"geometry, material, position, rotation, scale",Object3D; SkinnedMesh
30,Object,SkinnedMesh,"skinned, rigged, animation, bones",Mesh with skeletal animation,"skeleton, bind(), pose()",Mesh; Bone
31,Object,InstancedMesh,"instanced, performance, many, clone",Efficient multiple instance rendering,"count, setMatrixAt(), setColorAt()",Mesh; BatchedMesh
32,Object,Group,"group, container, parent, hierarchy",Container for grouping objects,"add(), remove(), children",Object3D; Scene
33,Object,Points,"points, particles, cloud",Point cloud rendering,"geometry, material",Mesh; Line
34,Object,Line,"line, path, wire",Line rendering,"geometry, material",Points; LineSegments
35,Object,Sprite,"sprite, billboard, always-facing",Always camera-facing plane,"material, center, scale",Points; Mesh
36,Animation,AnimationMixer,"mixer, animation, playback, control",Animation playback controller,"clipAction(), update(), stopAllAction()",AnimationClip; AnimationAction
37,Animation,AnimationClip,"clip, keyframes, track, animation data",Animation data container,"tracks, duration, resetDuration()",AnimationMixer; KeyframeTrack
38,Animation,AnimationAction,"action, play, pause, loop",Animation playback control,"play(), stop(), crossFadeTo(), setEffectiveWeight()",AnimationMixer; AnimationClip
39,Loader,GLTFLoader,"gltf, glb, loader, model",GLTF/GLB model loader,"load(), parse(), setDRACOLoader()",Loader; DRACOLoader
40,Loader,FBXLoader,"fbx, autodesk, animation",FBX model loader,"load(), parse()",Loader; GLTFLoader
41,Loader,OBJLoader,"obj, wavefront, simple",OBJ model loader,"load(), parse(), setMaterials()",Loader; MTLLoader
42,Loader,TextureLoader,"texture, image, map",Image texture loader,"load(), loadAsync()",Loader; CubeTextureLoader
43,Loader,DRACOLoader,"draco, compression, mesh",Draco compressed mesh decoder,"setDecoderPath(), preload()",GLTFLoader; Loader
44,Control,OrbitControls,"orbit, rotate, zoom, pan, camera",Orbiting camera controls,"update(), target, enableDamping",Controls; TrackballControls
45,Control,MapControls,"map, overhead, 2d, navigation",Map-style camera controls,"update(), target, screenSpacePanning",OrbitControls; Controls
46,Control,FirstPersonControls,"fps, first person, walk",First-person camera controls,"update(), movementSpeed, lookSpeed",Controls; PointerLockControls
47,Control,TransformControls,"transform, gizmo, move, rotate, scale",Object manipulation gizmo,"attach(), detach(), setMode()",Controls; DragControls
48,Math,Vector3,"vector, position, direction, xyz",3D vector math,"add(), sub(), multiply(), normalize(), length()",Vector2; Quaternion
49,Math,Quaternion,"quaternion, rotation, orientation",Rotation representation,"setFromEuler(), slerp(), multiply()",Vector3; Euler
50,Math,Matrix4,"matrix, transform, 4x4",4x4 transformation matrix,"multiply(), invert(), decompose()",Matrix3; Vector3
51,Math,Box3,"box, aabb, bounds, collision",Axis-aligned bounding box,"setFromObject(), containsPoint(), intersectsBox()",Sphere; Raycaster
52,Math,Raycaster,"raycast, pick, intersect, mouse",Ray intersection testing,"setFromCamera(), intersectObjects()",Box3; Vector3
53,Helper,AxesHelper,"axes, xyz, debug, orientation",XYZ axes visualization,"size parameter",Helper; GridHelper
54,Helper,GridHelper,"grid, floor, debug, reference",Grid plane visualization,"size, divisions, colors",Helper; AxesHelper
55,Helper,BoxHelper,"box, bounds, debug, wireframe",Bounding box visualization,"setFromObject(), update()",Helper; Box3Helper
56,Effect,EffectComposer,"composer, postprocessing, chain",Post-processing effect chain,"addPass(), render(), setSize()",Pass; ShaderPass
57,Effect,RenderPass,"render, scene, camera, base",Base scene render pass,"scene, camera",Pass; EffectComposer
58,Effect,UnrealBloomPass,"bloom, glow, emissive, hdr",Unreal-style bloom effect,"strength, radius, threshold",Pass; EffectComposer
59,Effect,SSAOPass,"ssao, ao, ambient occlusion",Screen-space ambient occlusion,"kernelRadius, minDistance, maxDistance",Pass; GTAOPass
60,Effect,OutlinePass,"outline, selection, highlight",Object outline effect,"selectedObjects, visibleEdgeColor",Pass; EffectComposer
ID,Category,Keywords,Description,Complexity Range,Example Count,Primary Use Cases,Related Categories
1,webgl,"webgl, rendering, standard, basic",Standard WebGL rendering examples with core Three.js features,Low-High,216,"games, visualization, interactive",webgl / postprocessing; webgl / advanced
2,webgl / postprocessing,"postprocessing, effects, bloom, ao, fxaa",Post-processing effects and image manipulation,Medium-High,27,"cinematic, stylized, photo-realistic",webgl; webgl / advanced
3,webgl / advanced,"advanced, buffergeometry, custom, shader, gpu",Advanced rendering techniques and low-level GPU access,High,48,"optimization, custom rendering, research",webgl; webgpu
4,webgpu (wip),"webgpu, compute, modern, next-gen, tsl",WebGPU-based rendering and compute shaders,Medium-High,190,"future-proof, compute, modern browsers",webgl / advanced
5,webaudio,"audio, spatial, 3d sound, positional",Spatial audio and sound visualization,Medium,4,"games, immersive, vr",webxr
6,webxr,"vr, ar, xr, immersive, headset",Virtual and augmented reality experiences,Medium-High,26,"vr games, ar apps, immersive",webaudio; webgpu
7,games,"games, gameplay, interactive",Complete game-like examples and mechanics,Medium-High,1,"game development, interactive",webxr; physics
8,physics,"physics, simulation, rigid body, collision",Physics simulation and rigid body dynamics,High,13,"games, simulation, realistic",games; webgpu
9,misc,"misc, experiments, special",Miscellaneous experiments and special techniques,Varies,20,"experimentation, learning",All
10,css2d,"css2d, labels, html overlay",CSS-based 2D overlays on 3D scenes,Low,1,"ui, labels, annotations",css3d
11,css3d,"css3d, dom, html, 3d transform",CSS 3D transforms with Three.js,Medium,6,"ui, transitions, websites",css2d
12,svg,"svg, vector, 2d",SVG rendering and vector graphics,Low-Medium,2,"icons, diagrams, 2d graphics",css2d
13,tests,"tests, debug, development",Testing and debugging utilities,Low-Medium,2,"development, debugging",All
ID,Use Case,Keywords,Recommended Examples,Complexity,Technologies,Description
1,Product Configurator,"configurator, e-commerce, product, 3d viewer",webgl_loader_gltf; webgl_materials_variations_physical; webgl_lights_rectarealight,Medium,"GLTF, PBR Materials, Environment Maps",Interactive product visualization with material/color options
2,Data Visualization,"data viz, charts, 3d graphs, analytics",webgl_buffergeometry_points; webgl_interactive_points; webgl_geometry_dynamic,Medium,"BufferGeometry, Points, Custom Shaders",3D data visualization and interactive charts
3,Architectural Visualization,"archviz, building, interior, real estate",webgl_loader_gltf; webgl_materials_envmaps_hdr; webgl_shadowmap_csm; webgl_postprocessing_ssr,High,"GLTF, HDR, CSM Shadows, SSR",Architectural and interior design visualization
4,Game Development,"game, fps, rpg, interactive",webgl_animation_skinning_blending; physics; webgpu_compute_birds; webgl_portal,High,"Animation, Physics, Compute, Controls",Game mechanics and interactive experiences
5,Medical Visualization,"medical, anatomy, ct scan, volume",webgl_loader_nrrd; webgl_volume_cloud; webgl_loader_pdb,High,"Volume Rendering, NRRD, PDB",Medical imaging and anatomical visualization
6,Scientific Visualization,"scientific, simulation, physics, research",webgl_gpgpu_protoplanet; webgl_gpgpu_water; webgpu_compute_particles,High,"GPGPU, Compute Shaders, Simulation",Scientific data and physics simulation
7,360 Panorama Viewer,"360, panorama, vr, virtual tour",webgl_panorama_equirectangular; webgl_video_panorama_equirectangular; webxr_vr_panorama,Low-Medium,"Equirectangular, Video, WebXR",360-degree photo and video viewing
8,E-commerce 3D,"e-commerce, shopping, ar, product view",webgl_loader_gltf; webgl_loader_usdz; webgl_materials_physical_transmission,Medium,"GLTF, USDZ, Transmission",Product viewing for online shopping
9,Character Animation,"character, avatar, animation, rigging",webgl_animation_keyframes; webgl_animation_skinning_ik; webgl_morphtargets_face,Medium-High,"Animation, IK, Morph Targets",Character animation and avatar systems
10,Particle Effects,"particles, effects, vfx, simulation",webgl_gpgpu_birds; webgl_points_dynamic; webgpu_compute_particles_rain,Medium-High,"GPGPU, Points, Compute",Particle systems and visual effects
11,CAD Viewer,"cad, engineering, step, iges",webgl_loader_3mf; webgl_loader_stl; webgl_geometry_nurbs; webgl_clipping,Medium,"Loaders, NURBS, Clipping",CAD and engineering model viewing
12,Virtual Reality,"vr, immersive, headset, quest",webxr_vr_sandbox; webxr_vr_teleport; webxr_vr_rollercoaster,High,"WebXR, Controllers, Hand Tracking",VR experiences and applications
13,Augmented Reality,"ar, mobile, camera, overlay",webxr_ar_cones; webxr_ar_hittest; webxr_ar_lighting,High,"WebXR AR, Hit Test, Lighting Estimation",AR experiences on mobile devices
14,Portfolio Website,"portfolio, creative, art, showcase",webgl_postprocessing_unreal_bloom; webgl_shaders_ocean; webgl_trails,Medium,"Post-processing, Custom Shaders, Effects",Creative portfolio and artistic showcases
15,Real-time Collaboration,"collaboration, multiplayer, networking",webgl_multiple_scenes_comparison; webgl_multiple_views,Medium,"Multiple Scenes, Sync, State",Multi-user collaborative 3D environments
16,Point Cloud Visualization,"point cloud, lidar, scan, photogrammetry",webgl_loader_pcd; webgl_loader_ply; webgl_points_sprites,Medium,"PCD, PLY, Point Rendering",Point cloud data visualization
17,Terrain and Landscape,"terrain, landscape, heightmap, outdoor",webgl_geometry_terrain; webgl_geometry_minecraft; webgl_water,Medium,"Heightmap, LOD, Water",Outdoor and terrain visualization
18,Car Configurator,"car, automotive, paint, configurator",webgl_materials_car; webgl_materials_physical_clearcoat; webgl_materials_envmaps_groundprojected,High,"Clearcoat, Environment, Reflection",Automotive visualization and configuration
19,Educational 3D,"education, learning, interactive, simulation",webgl_loader_pdb; webgl_morphtargets; webgl_geometry_text,Low-Medium,"Models, Animation, Interaction",Educational and learning applications
20,WebGPU Modern,"webgpu, modern, compute, next-gen",webgpu_compute_birds; webgpu_tsl_editor; webgpu_postprocessing_bloom,High,"WebGPU, TSL, Compute Shaders",Modern WebGPU-based applications
Three.js Fundamentals
Overview
Three.js scene setup, cameras, renderer, Object3D hierarchy, coordinate systems. Use when setting up 3D scenes, creating cameras, configuring renderers, managing object hierarchies, or working with transforms.
Quick Start
import * as THREE from 'three';
// Create scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// Add a mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Add light
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);
camera.position.z = 5;
// Animation loop
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// Handle resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});Core Classes
Scene
Container for all 3D objects, lights, and cameras.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); // Solid color
scene.background = texture; // Skybox texture
scene.background = cubeTexture; // Cubemap
scene.environment = envMap; // Environment map for PBR
scene.fog = new THREE.Fog(0xffffff, 1, 100); // Linear fog
scene.fog = new THREE.FogExp2(0xffffff, 0.02); // Exponential fogCameras
PerspectiveCamera - Most common, simulates human eye.
// PerspectiveCamera(fov, aspect, near, far)
const camera = new THREE.PerspectiveCamera(
75, // Field of view (degrees)
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near clipping plane
1000 // Far clipping plane
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
camera.updateProjectionMatrix(); // Call after changing fov, aspect, near, farOrthographicCamera - No perspective distortion, good for 2D/isometric.
// OrthographicCamera(left, right, top, bottom, near, far)
const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 10;
const camera = new THREE.OrthographicCamera((frustumSize * aspect) / -2, (frustumSize * aspect) / 2, frustumSize / 2, frustumSize / -2, 0.1, 1000);ArrayCamera - Multiple viewports with sub-cameras.
const cameras = [];
for (let i = 0; i < 4; i++) {
const subcamera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
subcamera.viewport = new THREE.Vector4(Math.floor(i % 2) * 0.5, Math.floor(i / 2) * 0.5, 0.5, 0.5);
cameras.push(subcamera);
}
const arrayCamera = new THREE.ArrayCamera(cameras);CubeCamera - Renders environment maps for reflections.
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256);
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
scene.add(cubeCamera);
// Use for reflections
material.envMap = cubeRenderTarget.texture;
// Update each frame (expensive!)
cubeCamera.position.copy(reflectiveMesh.position);
cubeCamera.update(renderer, scene);WebGLRenderer
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#canvas'), // Optional existing canvas
antialias: true, // Smooth edges
alpha: true, // Transparent background
powerPreference: 'high-performance', // GPU hint
preserveDrawingBuffer: true // For screenshots
});
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// Tone mapping
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
// Color space (Three.js r152+)
renderer.outputColorSpace = THREE.SRGBColorSpace;
// Shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Clear color
renderer.setClearColor(0x000000, 1);
// Render
renderer.render(scene, camera);Object3D
Base class for all 3D objects. Mesh, Group, Light, Camera all extend Object3D.
const obj = new THREE.Object3D();
// Transform
obj.position.set(x, y, z);
obj.rotation.set(x, y, z); // Euler angles (radians)
obj.quaternion.set(x, y, z, w); // Quaternion rotation
obj.scale.set(x, y, z);
// Local vs World transforms
obj.getWorldPosition(targetVector);
obj.getWorldQuaternion(targetQuaternion);
obj.getWorldDirection(targetVector);
// Hierarchy
obj.add(child);
obj.remove(child);
obj.parent;
obj.children;
// Visibility
obj.visible = false;
// Layers (for selective rendering/raycasting)
obj.layers.set(1);
obj.layers.enable(2);
obj.layers.disable(0);
// Traverse hierarchy
obj.traverse(child => {
if (child.isMesh) child.material.color.set(0xff0000);
});
// Matrix updates
obj.matrixAutoUpdate = true; // Default: auto-update matrices
obj.updateMatrix(); // Manual matrix update
obj.updateMatrixWorld(true); // Update world matrix recursivelyGroup
Empty container for organizing objects.
const group = new THREE.Group();
group.add(mesh1);
group.add(mesh2);
scene.add(group);
// Transform entire group
group.position.x = 5;
group.rotation.y = Math.PI / 4;Mesh
Combines geometry and material.
const mesh = new THREE.Mesh(geometry, material);
// Multiple materials (one per geometry group)
const mesh = new THREE.Mesh(geometry, [material1, material2]);
// Useful properties
mesh.geometry;
mesh.material;
mesh.castShadow = true;
mesh.receiveShadow = true;
// Frustum culling
mesh.frustumCulled = true; // Default: skip if outside camera view
// Render order
mesh.renderOrder = 10; // Higher = rendered laterCoordinate System
Three.js uses a right-handed coordinate system:
- +X points right
- +Y points up
- +Z points toward viewer (out of screen)
// Axes helper
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper); // Red=X, Green=Y, Blue=ZMath Utilities
Vector3
const v = new THREE.Vector3(x, y, z);
v.set(x, y, z);
v.copy(otherVector);
v.clone();
// Operations (modify in place)
v.add(v2);
v.sub(v2);
v.multiply(v2);
v.multiplyScalar(2);
v.divideScalar(2);
v.normalize();
v.negate();
v.clamp(min, max);
v.lerp(target, alpha);
// Calculations (return new value)
v.length();
v.lengthSq(); // Faster than length()
v.distanceTo(v2);
v.dot(v2);
v.cross(v2); // Modifies v
v.angleTo(v2);
// Transform
v.applyMatrix4(matrix);
v.applyQuaternion(q);
v.project(camera); // World to NDC
v.unproject(camera); // NDC to worldMatrix4
const m = new THREE.Matrix4();
m.identity();
m.copy(other);
m.clone();
// Build transforms
m.makeTranslation(x, y, z);
m.makeRotationX(theta);
m.makeRotationY(theta);
m.makeRotationZ(theta);
m.makeRotationFromQuaternion(q);
m.makeScale(x, y, z);
// Compose/decompose
m.compose(position, quaternion, scale);
m.decompose(position, quaternion, scale);
// Operations
m.multiply(m2); // m = m * m2
m.premultiply(m2); // m = m2 * m
m.invert();
m.transpose();
// Camera matrices
m.makePerspective(left, right, top, bottom, near, far);
m.makeOrthographic(left, right, top, bottom, near, far);
m.lookAt(eye, target, up);Quaternion
const q = new THREE.Quaternion();
q.setFromEuler(euler);
q.setFromAxisAngle(axis, angle);
q.setFromRotationMatrix(matrix);
q.multiply(q2);
q.slerp(target, t); // Spherical interpolation
q.normalize();
q.invert();Euler
const euler = new THREE.Euler(x, y, z, 'XYZ'); // Order matters!
euler.setFromQuaternion(q);
euler.setFromRotationMatrix(m);
// Rotation orders: 'XYZ', 'YXZ', 'ZXY', 'XZY', 'YZX', 'ZYX'Color
const color = new THREE.Color(0xff0000);
const color = new THREE.Color('red');
const color = new THREE.Color('rgb(255, 0, 0)');
const color = new THREE.Color('#ff0000');
color.setHex(0x00ff00);
color.setRGB(r, g, b); // 0-1 range
color.setHSL(h, s, l); // 0-1 range
color.lerp(otherColor, alpha);
color.multiply(otherColor);
color.multiplyScalar(2);MathUtils
THREE.MathUtils.clamp(value, min, max);
THREE.MathUtils.lerp(start, end, alpha);
THREE.MathUtils.mapLinear(value, inMin, inMax, outMin, outMax);
THREE.MathUtils.degToRad(degrees);
THREE.MathUtils.radToDeg(radians);
THREE.MathUtils.randFloat(min, max);
THREE.MathUtils.randInt(min, max);
THREE.MathUtils.smoothstep(x, min, max);
THREE.MathUtils.smootherstep(x, min, max);Common Patterns
Proper Cleanup
function dispose() {
// Dispose geometries
mesh.geometry.dispose();
// Dispose materials
if (Array.isArray(mesh.material)) {
mesh.material.forEach(m => m.dispose());
} else {
mesh.material.dispose();
}
// Dispose textures
texture.dispose();
// Remove from scene
scene.remove(mesh);
// Dispose renderer
renderer.dispose();
}Clock for Animation
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta(); // Time since last frame (seconds)
const elapsed = clock.getElapsedTime(); // Total time (seconds)
mesh.rotation.y += delta * 0.5; // Consistent speed regardless of framerate
requestAnimationFrame(animate);
renderer.render(scene, camera);
}Responsive Canvas
function onWindowResize() {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
window.addEventListener('resize', onWindowResize);Loading Manager
const manager = new THREE.LoadingManager();
manager.onStart = (url, loaded, total) => console.log('Started loading');
manager.onLoad = () => console.log('All loaded');
manager.onProgress = (url, loaded, total) => console.log(`${loaded}/${total}`);
manager.onError = url => console.error(`Error loading ${url}`);
const textureLoader = new THREE.TextureLoader(manager);
const gltfLoader = new GLTFLoader(manager);Performance Tips
1. Limit draw calls: Merge geometries, use instancing, atlas textures 2. Frustum culling: Enabled by default, ensure bounding boxes are correct 3. LOD (Level of Detail): Use THREE.LOD for distance-based mesh switching 4. Object pooling: Reuse objects instead of creating/destroying 5. Avoid `getWorldPosition` in loops: Cache results
// Merge static geometries
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geo1, geo2, geo3]);
// LOD
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0);
lod.addLevel(medDetailMesh, 50);
lod.addLevel(lowDetailMesh, 100);
scene.add(lod);See Also
threejs-geometry- Geometry creation and manipulationthreejs-materials- Material types and propertiesthreejs-lighting- Light types and shadows
Getting Started with Three.js
Core concepts for building your first 3D scene.
Essential Components
Every Three.js app needs 3 core elements:
1. Scene
Container for all 3D objects, lights, cameras.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); // black background
scene.fog = new THREE.Fog(0xffffff, 1, 5000); // distance fog2. Camera
Viewpoint into the 3D scene.
PerspectiveCamera (realistic, most common):
const camera = new THREE.PerspectiveCamera(
75, // fov - field of view in degrees
window.innerWidth / window.innerHeight, // aspect ratio
0.1, // near clipping plane
1000 // far clipping plane
);
camera.position.set(0, 0, 5);
camera.lookAt(0, 0, 0);OrthographicCamera (no perspective distortion):
const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);3. Renderer
Renders scene using camera perspective.
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);Basic Geometries
Primitive shapes ready to use:
// Box
new THREE.BoxGeometry(width, height, depth);
// Sphere
new THREE.SphereGeometry(radius, widthSegments, heightSegments);
// Plane
new THREE.PlaneGeometry(width, height);
// Cylinder
new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radialSegments);
// Cone
new THREE.ConeGeometry(radius, height, radialSegments);
// Torus
new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments);Basic Materials
Materials define surface appearance:
MeshBasicMaterial - unlit, flat color:
new THREE.MeshBasicMaterial({ color: 0xff0000 });MeshStandardMaterial - PBR, responds to lights:
new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
});MeshPhongMaterial - specular highlights:
new THREE.MeshPhongMaterial({
color: 0x0000ff,
shininess: 100
});Creating Mesh
Combine geometry + material:
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);Basic Lights
Materials (except Basic) need lights to be visible:
// Ambient - global illumination
const ambient = new THREE.AmbientLight(0x404040); // soft white
scene.add(ambient);
// Directional - sun-like, infinite distance
const directional = new THREE.DirectionalLight(0xffffff, 1);
directional.position.set(5, 5, 5);
scene.add(directional);
// Point - lightbulb, radiates in all directions
const point = new THREE.PointLight(0xff0000, 1, 100);
point.position.set(0, 10, 0);
scene.add(point);Animation Loop
Continuously render and update scene:
function animate() {
requestAnimationFrame(animate);
// Update objects
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// Render
renderer.render(scene, camera);
}
animate();Handle Window Resize
Keep aspect ratio correct:
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});Object3D Hierarchy
Transform and group objects:
const group = new THREE.Group();
group.add(cube1);
group.add(cube2);
scene.add(group);
// Transform
object.position.set(x, y, z);
object.rotation.set(x, y, z); // Euler angles
object.scale.set(x, y, z);
// Hierarchy transforms are relative to parentAsset Loading
Load 3D models, textures, and other assets.
Loading Manager
Coordinate multiple loads, track progress:
const manager = new THREE.LoadingManager();
manager.onStart = (url, loaded, total) => console.log('Loading:', url);
manager.onProgress = (url, loaded, total) => console.log(`${loaded}/${total}`);
manager.onLoad = () => console.log('Complete');
manager.onError = url => console.error('Error:', url);
const loader = new THREE.TextureLoader(manager);GLTF Loader (Recommended Format)
Industry standard, supports PBR materials, animations, bones:
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load(
'model.gltf',
gltf => {
scene.add(gltf.scene);
// Access animations
const mixer = new THREE.AnimationMixer(gltf.scene);
gltf.animations.forEach(clip => mixer.clipAction(clip).play());
},
xhr => console.log((xhr.loaded / xhr.total) * 100 + '% loaded'),
error => console.error(error)
);FBX Loader
Autodesk format, common in game dev:
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
const loader = new FBXLoader();
loader.load('model.fbx', object => {
scene.add(object);
});OBJ Loader
Simple geometry format:
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
const loader = new OBJLoader();
loader.load('model.obj', object => {
scene.add(object);
});
// With MTL (material library)
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
const mtlLoader = new MTLLoader();
mtlLoader.load('model.mtl', materials => {
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
objLoader.load('model.obj', object => scene.add(object));
});Texture Loader
Load images as textures:
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture.jpg');
// Use in material
const material = new THREE.MeshStandardMaterial({ map: texture });
// Load with callback
textureLoader.load(
'texture.jpg',
texture => {
material.map = texture;
material.needsUpdate = true;
},
xhr => console.log((xhr.loaded / xhr.total) * 100 + '% loaded'),
error => console.error(error)
);Cube Texture Loader
Load environment maps (skybox):
const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load([
'px.jpg',
'nx.jpg', // positive x, negative x
'py.jpg',
'ny.jpg', // positive y, negative y
'pz.jpg',
'nz.jpg' // positive z, negative z
]);
scene.background = envMap;
material.envMap = envMap;DRACO Compressed Models
Smaller file sizes for GLTF:
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('path/to/draco/');
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load('compressed.gltf', gltf => scene.add(gltf.scene));KTX2 Compressed Textures
GPU-optimized texture compression:
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
const ktx2Loader = new KTX2Loader();
ktx2Loader.setTranscoderPath('path/to/basis/');
ktx2Loader.detectSupport(renderer);
ktx2Loader.load('texture.ktx2', texture => {
material.map = texture;
material.needsUpdate = true;
});Common Other Loaders
// STL (3D printing)
import { STLLoader } from 'three/addons/loaders/STLLoader.js';
// Collada (.dae)
import { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js';
// 3DS Max
import { TDSLoader } from 'three/addons/loaders/TDSLoader.js';Best Practices
- Use GLTF/GLB for web (best compression, features)
- Compress with DRACO for large models
- Use KTX2 for textures (GPU-friendly)
- Enable caching:
THREE.Cache.enabled = true; - Show loading progress to users
- Handle errors gracefully
Textures
Map images and data onto 3D surfaces.
Texture Types
Standard 2D Texture
const texture = new THREE.Texture(image);
texture.needsUpdate = true; // required after manual creation
// Or use loader (auto-updates)
const texture = new THREE.TextureLoader().load('image.jpg');Canvas Texture
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Draw on canvas...
const texture = new THREE.CanvasTexture(canvas);Video Texture
const video = document.createElement('video');
video.src = 'video.mp4';
video.play();
const texture = new THREE.VideoTexture(video);Data Texture
const size = 512;
const data = new Uint8Array(size * size * 4);
// Fill data with RGBA values...
const texture = new THREE.DataTexture(data, size, size);
texture.needsUpdate = true;Cube Texture (Environment/Skybox)
const loader = new THREE.CubeTextureLoader();
const texture = loader.load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);Material Maps
Multiple texture types for different effects:
const material = new THREE.MeshStandardMaterial({
map: diffuseTexture, // base color
normalMap: normalTexture, // surface detail
roughnessMap: roughnessTexture, // surface roughness variation
metalnessMap: metalnessTexture, // metallic areas
aoMap: aoTexture, // ambient occlusion
emissiveMap: emissiveTexture, // glow areas
alphaMap: alphaTexture, // transparency
bumpMap: bumpTexture, // height variation
displacementMap: dispTexture // vertex displacement
});
// AO map requires second UV set
geometry.setAttribute('uv2', geometry.attributes.uv);Wrapping Modes
Control texture repeat behavior:
texture.wrapS = THREE.RepeatWrapping; // horizontal
texture.wrapT = THREE.RepeatWrapping; // vertical
// Options:
// THREE.RepeatWrapping - tile infinitely
// THREE.ClampToEdgeWrapping - stretch edge pixels
// THREE.MirroredRepeatWrapping - mirror on each repeat
// Set repeat count
texture.repeat.set(4, 4);
// Offset texture
texture.offset.set(0.5, 0.5);Filtering
Control texture sampling quality:
// Magnification (when texel < pixel)
texture.magFilter = THREE.LinearFilter; // smooth
// or THREE.NearestFilter // pixelated
// Minification (when texel > pixel)
texture.minFilter = THREE.LinearMipmapLinearFilter; // best quality
// Options:
// THREE.NearestFilter
// THREE.LinearFilter
// THREE.NearestMipmapNearestFilter
// THREE.NearestMipmapLinearFilter
// THREE.LinearMipmapNearestFilter
// THREE.LinearMipmapLinearFilter
// Anisotropic filtering (better at angles)
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();UV Mapping
Control how texture is mapped to geometry:
// Flip texture vertically
texture.flipY = false;
// Rotate texture
texture.rotation = Math.PI / 4; // 45 degrees
texture.center.set(0.5, 0.5); // rotation center
// Transform UV coordinates
const uvAttribute = geometry.attributes.uv;
for (let i = 0; i < uvAttribute.count; i++) {
let u = uvAttribute.getX(i);
let v = uvAttribute.getY(i);
uvAttribute.setXY(i, u * 2, v * 2); // scale UVs
}
uvAttribute.needsUpdate = true;Color Space
Handle color space correctly:
// For color data (diffuse, emissive)
texture.colorSpace = THREE.SRGBColorSpace;
// For non-color data (normal, roughness, etc.)
texture.colorSpace = THREE.NoColorSpace; // or LinearSRGBColorSpacePerformance Optimization
// Use mipmaps (auto-generated by default)
texture.generateMipmaps = true;
// Dispose when done
texture.dispose();
// Compress textures (use KTX2Loader for .ktx2 files)
// Reduce resolution for distant objects
// Use texture atlases to reduce draw callsAdvanced Textures
// 3D Texture (volumetric)
const texture3d = new THREE.Data3DTexture(data, width, height, depth);
// Depth Texture (for advanced effects)
const depthTexture = new THREE.DepthTexture(width, height);
// Compressed Texture
const compressedTexture = new THREE.CompressedTexture(...);Cameras
Define viewpoint and projection for rendering.
Perspective Camera
Realistic camera with field of view (most common):
const camera = new THREE.PerspectiveCamera(
fov, // field of view in degrees (typically 45-75)
aspect, // width / height
near, // near clipping plane (typically 0.1)
far // far clipping plane (typically 1000)
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// Update after changing parameters
camera.fov = 60;
camera.updateProjectionMatrix();Orthographic Camera
No perspective distortion (parallel projection):
const frustumSize = 10;
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2, // left
(frustumSize * aspect) / 2, // right
frustumSize / 2, // top
frustumSize / -2, // bottom
0.1, // near
1000 // far
);
// Useful for: 2D games, CAD, isometric viewsCamera Controls (Addons)
OrbitControls (Most Common)
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.enableDamping = true; // smooth motion
controls.dampingFactor = 0.05;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2; // prevent going below ground
// In animation loop
function animate() {
controls.update(); // required if enableDamping = true
renderer.render(scene, camera);
}FirstPersonControls
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
controls.update(delta);
renderer.render(scene, camera);
}FlyControls
import { FlyControls } from 'three/addons/controls/FlyControls.js';
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 24;
controls.dragToLook = true;TransformControls
import { TransformControls } from 'three/addons/controls/TransformControls.js';
const controls = new TransformControls(camera, renderer.domElement);
controls.attach(mesh);
scene.add(controls);
// Switch modes
controls.setMode('translate'); // or 'rotate', 'scale'
// Events
controls.addEventListener('change', () => renderer.render(scene, camera));
controls.addEventListener('dragging-changed', event => {
orbitControls.enabled = !event.value;
});Camera Methods
// Position and orientation
camera.position.set(x, y, z);
camera.lookAt(x, y, z); // or lookAt(vector3) or lookAt(object.position)
camera.up.set(0, 1, 0); // define "up" direction
// Get world direction
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
// Screen to world conversion
const mouse = new THREE.Vector2(x, y); // normalized device coords (-1 to 1)
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
// World to screen
const vector = new THREE.Vector3(x, y, z);
vector.project(camera); // now in normalized device coordsLayers
Selective rendering with layers:
// Set object layers
mesh.layers.set(1);
// Set camera layers
camera.layers.enable(0); // render layer 0
camera.layers.enable(1); // render layer 1
camera.layers.disable(2); // don't render layer 2
// Objects on disabled layers won't be renderedFrustum Culling
Automatic optimization (objects outside view are not rendered):
// Manually check if object is in view
const frustum = new THREE.Frustum();
const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(matrix);
if (frustum.containsPoint(object.position)) {
// Object is visible
}Multiple Cameras
const mainCamera = new THREE.PerspectiveCamera(...);
const minimapCamera = new THREE.OrthographicCamera(...);
// Render with different viewports
renderer.setViewport(0, 0, width, height);
renderer.render(scene, mainCamera);
renderer.setViewport(width - 200, height - 200, 200, 200);
renderer.render(scene, minimapCamera);Resize Handling
window.addEventListener('resize', () => {
// Perspective camera
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// Orthographic camera
const aspect = window.innerWidth / window.innerHeight;
camera.left = (-frustumSize * aspect) / 2;
camera.right = (frustumSize * aspect) / 2;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});Lights
Illuminate 3D scenes with various light types.
Ambient Light
Global illumination affecting all objects equally:
const light = new THREE.AmbientLight(0x404040); // soft white
scene.add(light);
// Often used as base illumination with other lightsDirectional Light
Infinite distance light with parallel rays (sun-like):
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 10, 5);
light.target.position.set(0, 0, 0);
scene.add(light);
scene.add(light.target); // target must be in scene
// With shadows
light.castShadow = true;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500;
light.shadow.camera.left = -10;
light.shadow.camera.right = 10;
light.shadow.camera.top = 10;
light.shadow.camera.bottom = -10;
// Visualize shadow camera
const helper = new THREE.CameraHelper(light.shadow.camera);
scene.add(helper);Point Light
Omnidirectional light from a point (lightbulb-like):
const light = new THREE.PointLight(0xff0000, 1, 100, 2);
// params: color, intensity, distance (0 = infinite), decay
light.position.set(0, 10, 0);
scene.add(light);
// With shadows
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 100;Spot Light
Cone-shaped light (spotlight-like):
const light = new THREE.SpotLight(0xffffff, 1);
light.position.set(0, 10, 0);
light.target.position.set(0, 0, 0);
scene.add(light);
scene.add(light.target);
// Cone parameters
light.angle = Math.PI / 6; // cone angle
light.penumbra = 0.1; // edge softness (0-1)
light.decay = 2; // light falloff
light.distance = 100; // max range (0 = infinite)
// With shadows
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;Hemisphere Light
Sky/ground two-color lighting:
const light = new THREE.HemisphereLight(
0x0000ff, // sky color (blue)
0x00ff00, // ground color (green)
0.6 // intensity
);
scene.add(light);
// Good for outdoor scenesRectArea Light (Addon)
Rectangular area light (realistic surface illumination):
import { RectAreaLight } from 'three/addons/lights/RectAreaLight.js';
const light = new RectAreaLight(0xffffff, 5, 10, 10);
// params: color, intensity, width, height
light.position.set(0, 5, 0);
light.lookAt(0, 0, 0);
scene.add(light);
// Requires WebGL 2.0Shadow Configuration
Global renderer settings:
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // soft shadows
// Shadow types:
// THREE.BasicShadowMap - fast, aliased
// THREE.PCFShadowMap - smoother
// THREE.PCFSoftShadowMap - softer (default)
// THREE.VSMShadowMap - variance shadow maps
// Objects must opt-in to shadows
mesh.castShadow = true; // object casts shadows
mesh.receiveShadow = true; // object receives shadowsLight Helpers
Visualize light positions and directions:
// Directional light
const helper = new THREE.DirectionalLightHelper(light, 5);
scene.add(helper);
// Point light
const helper = new THREE.PointLightHelper(light, 1);
scene.add(helper);
// Spot light
const helper = new THREE.SpotLightHelper(light);
scene.add(helper);
// Hemisphere light
const helper = new THREE.HemisphereLightHelper(light, 5);
scene.add(helper);
// RectArea light
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
const helper = new RectAreaLightHelper(light);
light.add(helper);Light Intensity & Units
// Intensity values depend on physically-based rendering:
// - Lower values (0.1-1) for ambient/hemisphere
// - Higher values (1-10) for directional/point/spot
// - Very high (10-100+) for small area lights
// Physical light units (optional)
renderer.physicallyCorrectLights = true; // deprecated in newer versions
// Use intensity in candelas (cd) for point/spot lightsPerformance Tips
- Limit number of lights (3-5 for good performance)
- Use ambient + 1-2 directional lights for outdoor scenes
- Bake lighting into textures for static scenes
- Use lightmaps for complex static lighting
- Shadows are expensive - use selectively
- Lower shadow map resolution for better performance
Animations
Animate objects, cameras, and imported models.
Animation System
Three.js uses AnimationMixer for playback:
// Create mixer for object
const mixer = new THREE.AnimationMixer(object);
// Play animation clip
const action = mixer.clipAction(animationClip);
action.play();
// Update in render loop
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
mixer.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Loading Animations
From GLTF/FBX files:
const loader = new GLTFLoader();
loader.load('model.gltf', gltf => {
scene.add(gltf.scene);
const mixer = new THREE.AnimationMixer(gltf.scene);
// Play all animations
gltf.animations.forEach(clip => {
mixer.clipAction(clip).play();
});
// Or play specific animation
const clip = THREE.AnimationClip.findByName(gltf.animations, 'Walk');
const action = mixer.clipAction(clip);
action.play();
});Animation Actions
Control playback:
const action = mixer.clipAction(clip);
// Playback control
action.play();
action.stop();
action.pause();
action.reset();
// Loop modes
action.setLoop(THREE.LoopRepeat, Infinity); // loop forever
action.setLoop(THREE.LoopOnce, 1); // play once, stop at end
action.setLoop(THREE.LoopPingPong, Infinity); // reverse on each loop
// Speed control
action.timeScale = 1.5; // 1.5x speed
action.timeScale = -1; // reverse
// Weight (for blending)
action.setEffectiveWeight(0.5); // 50% influence
// Enable/disable
action.enabled = true;Animation Blending
Smooth transitions between animations:
// Crossfade between two actions
currentAction.crossFadeTo(nextAction, 0.5, true); // 0.5 second transition
// Or manually control weights
currentAction.fadeOut(0.5);
nextAction.reset().fadeIn(0.5).play();Creating Custom Animations
Using KeyframeTracks:
// Position animation
const times = [0, 1, 2]; // keyframe times in seconds
const values = [0, 0, 0, 10, 0, 0, 0, 0, 0]; // x,y,z for each time
const positionKF = new THREE.VectorKeyframeTrack(
'.position', // property path
times,
values
);
// Rotation animation (quaternions)
const quaternion1 = new THREE.Quaternion();
const quaternion2 = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI, 0));
const rotationKF = new THREE.QuaternionKeyframeTrack(
'.quaternion',
[0, 1],
[quaternion1.x, quaternion1.y, quaternion1.z, quaternion1.w, quaternion2.x, quaternion2.y, quaternion2.z, quaternion2.w]
);
// Create clip from tracks
const clip = new THREE.AnimationClip('custom', 2, [positionKF, rotationKF]);
const mixer = new THREE.AnimationMixer(object);
mixer.clipAction(clip).play();Keyframe Track Types
// Different track types for different properties
new THREE.VectorKeyframeTrack('.position', times, values);
new THREE.VectorKeyframeTrack('.scale', times, values);
new THREE.QuaternionKeyframeTrack('.quaternion', times, values);
new THREE.ColorKeyframeTrack('.material.color', times, values);
new THREE.NumberKeyframeTrack('.material.opacity', times, values);
new THREE.BooleanKeyframeTrack('.visible', times, values);Skeletal Animation
For rigged characters:
// Object must be SkinnedMesh with skeleton
const mesh = gltf.scene.children.find(child => child.isSkinnedMesh);
// Access bones
const skeleton = mesh.skeleton;
const bones = skeleton.bones;
// Manually control bones
bones[0].rotation.x = Math.PI / 4;
// Use SkeletonHelper to visualize
const helper = new THREE.SkeletonHelper(mesh);
scene.add(helper);Morph Target Animation
Blend shapes:
// Morph targets are defined in geometry
const mesh = new THREE.Mesh(geometry, material);
// Animate morph influences
mesh.morphTargetInfluences[0] = 0.5; // 50% of first morph target
// Create animation clip for morphs
const track = new THREE.NumberKeyframeTrack('.morphTargetInfluences[0]', [0, 1, 2], [0, 1, 0]);
const clip = new THREE.AnimationClip('morph', 2, [track]);Manual Animation
Simple transform animations:
const clock = new THREE.Clock();
function animate() {
const elapsed = clock.getElapsedTime();
// Rotate
object.rotation.y = elapsed;
// Oscillate position
object.position.y = Math.sin(elapsed * 2) * 5;
// Pulse scale
const scale = 1 + Math.sin(elapsed * 3) * 0.1;
object.scale.set(scale, scale, scale);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Tween Libraries
For complex easing (use with external lib like GSAP):
// With GSAP
gsap.to(object.position, {
duration: 1,
x: 10,
ease: 'power2.inOut'
});Math Utilities
Essential mathematical objects for 3D programming.
Vector3
3D position, direction, or scale:
const v = new THREE.Vector3(x, y, z);
// Operations
v.add(otherVector);
v.sub(otherVector);
v.multiply(otherVector);
v.multiplyScalar(scalar);
v.divide(otherVector);
v.divideScalar(scalar);
// Analysis
v.length(); // magnitude
v.lengthSq(); // magnitude squared (faster)
v.normalize(); // make length = 1
v.dot(otherVector); // dot product
v.cross(otherVector); // cross product
v.distanceTo(otherVector);
v.angleTo(otherVector);
// Interpolation
v.lerp(targetVector, alpha); // linear interpolation
v.lerpVectors(v1, v2, alpha);
// Clamping
v.clamp(minVector, maxVector);
v.clampLength(minLength, maxLength);Vector2 & Vector4
Similar to Vector3 but 2D and 4D:
const v2 = new THREE.Vector2(x, y);
const v4 = new THREE.Vector4(x, y, z, w);Quaternion
Rotation representation (avoids gimbal lock):
const q = new THREE.Quaternion(x, y, z, w);
// From Euler angles
q.setFromEuler(new THREE.Euler(x, y, z, 'XYZ'));
// From axis-angle
const axis = new THREE.Vector3(0, 1, 0);
q.setFromAxisAngle(axis, Math.PI / 2);
// From rotation matrix
q.setFromRotationMatrix(matrix);
// Interpolation
q.slerp(targetQuaternion, alpha); // spherical linear interpolation
// Apply to vector
const v = new THREE.Vector3(1, 0, 0);
v.applyQuaternion(q);Euler
Rotation as XYZ angles (degrees):
const euler = new THREE.Euler(x, y, z, 'XYZ');
// Order: 'XYZ', 'YXZ', 'ZXY', 'ZYX', 'YZX', 'XZY'
// From quaternion
euler.setFromQuaternion(q);
// From rotation matrix
euler.setFromRotationMatrix(matrix);
// Apply to object
object.rotation.copy(euler);Matrix4
4x4 transformation matrix:
const m = new THREE.Matrix4();
// Compose transformation
m.compose(position, quaternion, scale);
// Decompose
const pos = new THREE.Vector3();
const quat = new THREE.Quaternion();
const scale = new THREE.Vector3();
m.decompose(pos, quat, scale);
// Transform operations
m.makeTranslation(x, y, z);
m.makeRotationX(theta);
m.makeRotationY(theta);
m.makeRotationZ(theta);
m.makeScale(x, y, z);
// Combine matrices
m.multiply(otherMatrix);
m.premultiply(otherMatrix);
// Invert
m.invert();
// Apply to vector
const v = new THREE.Vector3(1, 2, 3);
v.applyMatrix4(m);Color
Color manipulation:
const color = new THREE.Color(0xff0000); // hex
const color = new THREE.Color('red'); // CSS
const color = new THREE.Color(1, 0, 0); // RGB 0-1
// Conversions
color.getHex(); // 0xff0000
color.getHexString(); // "ff0000"
color.getStyle(); // "rgb(255,0,0)"
// Color spaces
color.setHSL(h, s, l); // hue, saturation, lightness
const hsl = {};
color.getHSL(hsl); // fills hsl object
// Operations
color.add(otherColor);
color.multiply(otherColor);
color.lerp(targetColor, alpha);Raycaster
Ray intersection testing:
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// Convert mouse to normalized device coordinates
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Set ray from camera
raycaster.setFromCamera(mouse, camera);
// Find intersections
const intersects = raycaster.intersectObjects(scene.children, true);
// recursive = true to check children
if (intersects.length > 0) {
const hit = intersects[0];
console.log(hit.object); // intersected object
console.log(hit.point); // intersection point (Vector3)
console.log(hit.distance); // distance from camera
console.log(hit.face); // intersected face
}Box3
Axis-aligned bounding box:
const box = new THREE.Box3();
// From object
box.setFromObject(mesh);
// From points
box.setFromPoints(arrayOfVector3);
// Properties
box.min; // Vector3
box.max; // Vector3
box.getCenter(target); // fills target Vector3
box.getSize(target); // fills target Vector3
// Tests
box.containsPoint(point);
box.intersectsBox(otherBox);Sphere
Bounding sphere:
const sphere = new THREE.Sphere(center, radius);
// From box
sphere.setFromPoints(arrayOfVector3);
// From object
const box = new THREE.Box3().setFromObject(mesh);
box.getBoundingSphere(sphere);
// Tests
sphere.containsPoint(point);
sphere.intersectsSphere(otherSphere);Plane
Infinite plane:
const plane = new THREE.Plane(normal, constant);
// normal: Vector3, constant: distance from origin
// From coplanar points
plane.setFromCoplanarPoints(p1, p2, p3);
// Distance to point
plane.distanceToPoint(point);
// Project point onto plane
const projected = new THREE.Vector3();
plane.projectPoint(point, projected);Curves
Parametric curves:
// Bezier curve
const curve = new THREE.CubicBezierCurve3(
new THREE.Vector3(-10, 0, 0),
new THREE.Vector3(-5, 15, 0),
new THREE.Vector3(20, 15, 0),
new THREE.Vector3(10, 0, 0)
);
// Sample points
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
// Get point at t (0-1)
const point = curve.getPoint(0.5);Interaction & Picking
Handle user input and object interaction.
Mouse/Touch Raycasting
Detect which object user clicked:
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const clickableObjects = []; // array of meshes
function onPointerMove(event) {
// Normalize mouse coordinates (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Update raycaster
raycaster.setFromCamera(mouse, camera);
// Find intersections
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
// Hover effect
intersects[0].object.material.emissive.setHex(0xff0000);
}
}
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
const object = intersects[0].object;
console.log('Clicked:', object.name);
console.log('Point:', intersects[0].point);
}
}
renderer.domElement.addEventListener('pointermove', onPointerMove);
renderer.domElement.addEventListener('click', onClick);DragControls (Addon)
Drag objects with mouse:
import { DragControls } from 'three/addons/controls/DragControls.js';
const controls = new DragControls(objectsArray, camera, renderer.domElement);
// Events
controls.addEventListener('dragstart', event => {
orbitControls.enabled = false; // disable camera controls during drag
event.object.material.emissive.set(0xaaaaaa);
});
controls.addEventListener('drag', event => {
console.log(event.object.position);
});
controls.addEventListener('dragend', event => {
orbitControls.enabled = true;
event.object.material.emissive.set(0x000000);
});TransformControls (Addon)
Interactive 3D gizmo for translate/rotate/scale:
import { TransformControls } from 'three/addons/controls/TransformControls.js';
const transformControls = new TransformControls(camera, renderer.domElement);
scene.add(transformControls);
// Attach to object
transformControls.attach(mesh);
// Switch modes
transformControls.setMode('translate'); // or 'rotate', 'scale'
// Switch space
transformControls.setSpace('world'); // or 'local'
// Events
transformControls.addEventListener('change', () => {
renderer.render(scene, camera);
});
transformControls.addEventListener('dragging-changed', event => {
orbitControls.enabled = !event.value; // disable orbit during transform
});
// Keyboard shortcuts
window.addEventListener('keydown', event => {
switch (event.key) {
case 'g':
transformControls.setMode('translate');
break;
case 'r':
transformControls.setMode('rotate');
break;
case 's':
transformControls.setMode('scale');
break;
case 'x':
transformControls.showX = !transformControls.showX;
break;
case 'Escape':
transformControls.detach();
break;
}
});Selection Box (Addon)
Box selection for multiple objects:
import { SelectionBox } from 'three/addons/interactive/SelectionBox.js';
import { SelectionHelper } from 'three/addons/interactive/SelectionHelper.js';
const selectionBox = new SelectionBox(camera, scene);
const helper = new SelectionHelper(renderer, 'selectBox');
let isSelecting = false;
renderer.domElement.addEventListener('pointerdown', event => {
isSelecting = true;
selectionBox.startPoint.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
});
renderer.domElement.addEventListener('pointermove', event => {
if (isSelecting) {
selectionBox.endPoint.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
const allSelected = selectionBox.select();
console.log('Selected:', allSelected.length);
}
});
renderer.domElement.addEventListener('pointerup', () => {
isSelecting = false;
});Keyboard Input
Handle keyboard controls:
const keysPressed = {};
window.addEventListener('keydown', event => {
keysPressed[event.key] = true;
});
window.addEventListener('keyup', event => {
keysPressed[event.key] = false;
});
// In animation loop
function animate() {
const speed = 0.1;
if (keysPressed['w']) object.position.z -= speed;
if (keysPressed['s']) object.position.z += speed;
if (keysPressed['a']) object.position.x -= speed;
if (keysPressed['d']) object.position.x += speed;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Pointer Lock (First Person)
Lock pointer for FPS controls:
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
const controls = new PointerLockControls(camera, renderer.domElement);
// Lock on click
renderer.domElement.addEventListener('click', () => {
controls.lock();
});
controls.addEventListener('lock', () => {
console.log('Pointer locked');
});
controls.addEventListener('unlock', () => {
console.log('Pointer unlocked');
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
function animate() {
if (controls.isLocked) {
// Apply movement
controls.moveForward(velocity.z);
controls.moveRight(velocity.x);
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Object Highlighting
Visual feedback on hover/selection:
let hoveredObject = null;
const originalEmissive = new THREE.Color();
function onPointerMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
// Reset previous
if (hoveredObject) {
hoveredObject.material.emissive.copy(originalEmissive);
hoveredObject = null;
}
// Highlight new
if (intersects.length > 0) {
hoveredObject = intersects[0].object;
originalEmissive.copy(hoveredObject.material.emissive);
hoveredObject.material.emissive.setHex(0x555555);
}
renderer.domElement.style.cursor = hoveredObject ? 'pointer' : 'default';
}Tooltips & UI Overlays
Show HTML tooltip at 3D position:
function updateTooltip(object3D, text) {
const vector = object3D.position.clone();
vector.project(camera);
const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
const y = (-vector.y * 0.5 + 0.5) * window.innerHeight;
tooltip.style.left = x + 'px';
tooltip.style.top = y + 'px';
tooltip.textContent = text;
}Post-Processing
Apply visual effects after rendering.
EffectComposer Setup
Post-processing pipeline (addon):
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
// Create composer
const composer = new EffectComposer(renderer);
// Add render pass (required first pass)
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// Add effect passes
// ... (see below)
// Add output pass (required last pass)
const outputPass = new OutputPass();
composer.addPass(outputPass);
// Render with composer instead of renderer
function animate() {
requestAnimationFrame(animate);
composer.render();
}
// Handle resize
window.addEventListener('resize', () => {
composer.setSize(window.innerWidth, window.innerHeight);
});Bloom Effect
Glow effect for bright areas:
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold (brightness trigger)
);
composer.addPass(bloomPass);
// Adjust parameters
bloomPass.strength = 2.0;
bloomPass.radius = 1.0;
bloomPass.threshold = 0.5;SSAO (Screen Space Ambient Occlusion)
Realistic shadowing in crevices:
import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js';
const ssaoPass = new SSAOPass(scene, camera, width, height);
ssaoPass.kernelRadius = 16;
ssaoPass.minDistance = 0.005;
ssaoPass.maxDistance = 0.1;
composer.addPass(ssaoPass);SSR (Screen Space Reflections)
Real-time reflections:
import { SSRPass } from 'three/addons/postprocessing/SSRPass.js';
const ssrPass = new SSRPass({
renderer,
scene,
camera,
width: window.innerWidth,
height: window.innerHeight
});
ssrPass.opacity = 0.5;
ssrPass.maxDistance = 0.1;
composer.addPass(ssrPass);Depth of Field (Bokeh)
Blur based on depth:
import { BokehPass } from 'three/addons/postprocessing/BokehPass.js';
const bokehPass = new BokehPass(scene, camera, {
focus: 10.0, // focal distance
aperture: 0.025, // blur amount
maxblur: 0.01 // max blur size
});
composer.addPass(bokehPass);FXAA (Anti-Aliasing)
Smooth jagged edges:
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';
const fxaaPass = new ShaderPass(FXAAShader);
fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth;
fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight;
composer.addPass(fxaaPass);Outline Pass
Highlight selected objects:
import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js';
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
outlinePass.edgeStrength = 3;
outlinePass.edgeGlow = 0.5;
outlinePass.edgeThickness = 1;
outlinePass.visibleEdgeColor.set('#ffffff');
outlinePass.hiddenEdgeColor.set('#190a05');
// Set objects to outline
outlinePass.selectedObjects = [mesh1, mesh2];
composer.addPass(outlinePass);Film/Grain Effect
Add film grain and scanlines:
import { FilmPass } from 'three/addons/postprocessing/FilmPass.js';
const filmPass = new FilmPass(
0.35, // noise intensity
0.5, // scanline intensity
648, // scanline count
false // grayscale
);
composer.addPass(filmPass);Glitch Effect
Digital glitch distortion:
import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';
const glitchPass = new GlitchPass();
composer.addPass(glitchPass);Custom Shader Pass
Create custom effects:
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
const customShader = {
uniforms: {
tDiffuse: { value: null },
amount: { value: 1.0 }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float amount;
varying vec2 vUv;
void main() {
vec4 color = texture2D(tDiffuse, vUv);
// Apply custom effect
color.r *= amount;
gl_FragColor = color;
}
`
};
const customPass = new ShaderPass(customShader);
customPass.material.uniforms.amount.value = 1.5;
composer.addPass(customPass);Common Pass Patterns
// Combine multiple effects
composer.addPass(renderPass);
composer.addPass(ssaoPass);
composer.addPass(bloomPass);
composer.addPass(fxaaPass);
composer.addPass(outputPass);
// Selective rendering
bloomPass.renderToScreen = false; // render to texture, not screen
// Clear pass
import { ClearPass } from 'three/addons/postprocessing/ClearPass.js';
const clearPass = new ClearPass();
composer.addPass(clearPass);Performance Tips
- Post-processing is GPU-intensive
- Use lower resolution for expensive effects (SSAO, SSR)
- Limit number of passes (3-5 for good performance)
- Disable passes when not needed
- Use FXAA instead of MSAA (cheaper)
- Test on target devices
Camera Controls (Addons)
Interactive camera navigation systems.
OrbitControls (Most Common)
Orbit camera around a target:
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
// Target point
controls.target.set(0, 0, 0);
// Damping (smooth motion)
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Zoom limits
controls.minDistance = 5;
controls.maxDistance = 50;
// Rotation limits
controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = Math.PI / 2; // prevent going below ground
controls.minAzimuthAngle = -Math.PI / 4; // horizontal limit
controls.maxAzimuthAngle = Math.PI / 4;
// Behavior
controls.enablePan = true;
controls.enableZoom = true;
controls.enableRotate = true;
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;
// Mouse buttons
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.PAN
};
// In animation loop (required if damping enabled)
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
// Events
controls.addEventListener('change', () => {
renderer.render(scene, camera);
});MapControls
Bird's-eye map navigation (like OrbitControls but different mouse behavior):
import { MapControls } from 'three/addons/controls/MapControls.js';
const controls = new MapControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.screenSpacePanning = false;
controls.maxPolarAngle = Math.PI / 2;
// Mouse buttons
controls.mouseButtons = {
LEFT: THREE.MOUSE.PAN,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.ROTATE
};FirstPersonControls
FPS-style camera movement:
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = 1.0;
controls.verticalMax = 2.0;
// Requires delta time
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
controls.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}FlyControls
Free-form flying navigation:
import { FlyControls } from 'three/addons/controls/FlyControls.js';
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = Math.PI / 24;
controls.autoForward = false;
controls.dragToLook = false;
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
controls.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}PointerLockControls
Locked pointer FPS controls:
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
const controls = new PointerLockControls(camera, renderer.domElement);
// Lock pointer on click
renderer.domElement.addEventListener('click', () => {
controls.lock();
});
controls.addEventListener('lock', () => {
console.log('Locked');
});
controls.addEventListener('unlock', () => {
console.log('Unlocked');
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
window.addEventListener('keydown', event => {
switch (event.code) {
case 'KeyW':
moveForward = true;
break;
case 'KeyS':
moveBackward = true;
break;
case 'KeyA':
moveLeft = true;
break;
case 'KeyD':
moveRight = true;
break;
}
});
function animate() {
if (controls.isLocked) {
// Calculate movement
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize();
controls.moveForward(direction.z * 10);
controls.moveRight(direction.x * 10);
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}TrackballControls
Intuitive rotation (no gimbal lock):
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
const controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}ArcballControls
3D rotation with virtual ball metaphor:
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
const controls = new ArcballControls(camera, renderer.domElement, scene);
controls.enablePan = true;
controls.enableZoom = true;
controls.enableRotate = true;
controls.cursorZoom = true;
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Controls Comparison
OrbitControls: Product viewers, 3D models, general use MapControls: Top-down maps, strategy games FirstPersonControls: Architectural walkthroughs FlyControls: Space navigation, creative tools PointerLockControls: FPS games TrackballControls: CAD applications ArcballControls: Scientific visualization
Common Patterns
// Disable controls during UI interaction
transformControls.addEventListener('dragging-changed', event => {
orbitControls.enabled = !event.value;
});
// Reset camera position
function resetCamera() {
controls.reset();
}
// Animate camera to position
function moveCameraTo(position, target) {
gsap.to(camera.position, {
duration: 1,
x: position.x,
y: position.y,
z: position.z,
onUpdate: () => controls.update()
});
gsap.to(controls.target, {
duration: 1,
x: target.x,
y: target.y,
z: target.z
});
}Three.js - Advanced Materials
PBR materials and custom shaders.
MeshStandardMaterial (PBR)
Physically-based rendering with metallic/roughness workflow:
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.5, // 0 = dielectric, 1 = metal
roughness: 0.5, // 0 = smooth/shiny, 1 = rough/matte
map: colorTexture, // base color
normalMap: normalTexture, // surface detail
roughnessMap: roughnessTexture,
metalnessMap: metalnessTexture,
aoMap: aoTexture, // ambient occlusion
emissive: 0xff0000, // glow color
emissiveMap: emissiveTexture,
emissiveIntensity: 1.0,
envMap: environmentMap, // reflections
envMapIntensity: 1.0,
alphaMap: alphaTexture, // transparency control
transparent: true,
opacity: 1.0,
side: THREE.DoubleSide, // render both sides
flatShading: false // smooth normals
});MeshPhysicalMaterial (Enhanced PBR)
Extended PBR with clearcoat, transmission, sheen:
const material = new THREE.MeshPhysicalMaterial({
// All MeshStandardMaterial properties plus:
// Clearcoat (protective layer)
clearcoat: 1.0,
clearcoatRoughness: 0.1,
clearcoatMap: clearcoatTexture,
clearcoatRoughnessMap: clearcoatRoughTexture,
clearcoatNormalMap: clearcoatNormalTexture,
// Transmission (transparency with refraction)
transmission: 1.0, // 0-1, glass-like
thickness: 0.5, // volumetric thickness
ior: 1.5, // index of refraction (glass = 1.5)
// Sheen (fabric-like edge glow)
sheen: 1.0,
sheenRoughness: 0.5,
sheenColor: new THREE.Color(0xffffff),
// Iridescence (rainbow effect)
iridescence: 1.0,
iridescenceIOR: 1.3,
iridescenceThicknessRange: [100, 400],
// Anisotropy (directional reflections)
anisotropy: 1.0,
anisotropyRotation: 0
});ShaderMaterial (Custom Shaders)
Full control over vertex and fragment shaders:
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 },
color: { value: new THREE.Color(0xff0000) },
texture1: { value: texture }
},
vertexShader: `
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vUv = uv;
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
uniform sampler2D texture1;
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vec4 texColor = texture2D(texture1, vUv);
vec3 light = vec3(0.5, 0.2, 1.0);
float dProd = max(0.0, dot(vNormal, light));
gl_FragColor = vec4(color * dProd * texColor.rgb, 1.0);
}
`,
transparent: true,
side: THREE.DoubleSide
});
// Update uniform in animation loop
material.uniforms.time.value += 0.01;RawShaderMaterial
Like ShaderMaterial but without Three.js shader injection:
const material = new THREE.RawShaderMaterial({
uniforms: {
// ...
},
vertexShader: `
precision mediump float;
precision mediump int;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision mediump float;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv, 0.0, 1.0);
}
`
});Common Shader Patterns
Fresnel Effect
// In fragment shader
float fresnel = pow(1.0 - dot(vNormal, vViewDirection), 3.0);
gl_FragColor = vec4(mix(baseColor, edgeColor, fresnel), 1.0);Noise/Distortion
// Simple noise function
float noise(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
// UV distortion
vec2 distortedUV = vUv + vec2(
noise(vUv + time) * 0.1,
noise(vUv.yx + time) * 0.1
);Scrolling Texture
uniform float time;
varying vec2 vUv;
vec2 scrollUV = vUv + vec2(time * 0.1, 0.0);
vec4 color = texture2D(map, scrollUV);Material Blending
material.blending = THREE.AdditiveBlending;
// Options:
// THREE.NoBlending
// THREE.NormalBlending (default)
// THREE.AdditiveBlending (glow/light effects)
// THREE.SubtractiveBlending
// THREE.MultiplyBlending
// Custom blending
material.blending = THREE.CustomBlending;
material.blendEquation = THREE.AddEquation;
material.blendSrc = THREE.SrcAlphaFactor;
material.blendDst = THREE.OneMinusSrcAlphaFactor;Depth & Stencil
// Depth testing
material.depthTest = true;
material.depthWrite = true;
material.depthFunc = THREE.LessEqualDepth;
// Alpha testing (discard transparent pixels)
material.alphaTest = 0.5;
// Render order
mesh.renderOrder = 1; // higher renders later
// Polygonoffset (prevent z-fighting)
material.polygonOffset = true;
material.polygonOffsetFactor = 1;
material.polygonOffsetUnits = 1;Material Cloning & Disposal
// Clone material
const material2 = material.clone();
// Dispose when done (free GPU memory)
material.dispose();
texture.dispose();
geometry.dispose();Common Built-in Uniforms
Available in ShaderMaterial (automatic):
// Matrices
uniform mat4 modelMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat3 normalMatrix;
// Camera
uniform vec3 cameraPosition;
// Attributes (vertex shader)
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
attribute vec2 uv2;Performance Tips
- Use MeshStandardMaterial for most cases (good balance)
- MeshPhysicalMaterial is expensive (use sparingly)
- ShaderMaterial requires GPU knowledge
- Avoid transparent materials when possible
- Use alphaTest instead of transparency for cutouts
- Minimize uniform updates
- Share materials between meshes
Three.js Materials
Overview
Three.js materials - PBR, basic, phong, shader materials, material properties. Use when styling meshes, working with textures, creating custom shaders, or optimizing material performance.
Quick Start
import * as THREE from 'three';
// PBR material (recommended for realistic rendering)
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.5
});
const mesh = new THREE.Mesh(geometry, material);Material Types Overview
| Material | Use Case | Lighting |
|---|---|---|
| MeshBasicMaterial | Unlit, flat colors, wireframes | No |
| MeshLambertMaterial | Matte surfaces, performance | Yes (diffuse only) |
| MeshPhongMaterial | Shiny surfaces, specular highlights | Yes |
| MeshStandardMaterial | PBR, realistic materials | Yes (PBR) |
| MeshPhysicalMaterial | Advanced PBR, clearcoat, transmission | Yes (PBR+) |
| MeshToonMaterial | Cel-shaded, cartoon look | Yes (toon) |
| MeshNormalMaterial | Debug normals | No |
| MeshDepthMaterial | Depth visualization | No |
| ShaderMaterial | Custom GLSL shaders | Custom |
| RawShaderMaterial | Full shader control | Custom |
MeshBasicMaterial
No lighting calculations. Fast, always visible.
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide, // FrontSide, BackSide, DoubleSide
wireframe: false,
map: texture, // Color/diffuse texture
alphaMap: alphaTexture, // Transparency texture
envMap: envTexture, // Reflection texture
reflectivity: 1, // Env map intensity
fog: true // Affected by scene fog
});MeshLambertMaterial
Diffuse-only lighting. Fast, no specular highlights.
const material = new THREE.MeshLambertMaterial({
color: 0x00ff00,
emissive: 0x111111, // Self-illumination color
emissiveIntensity: 1,
map: texture,
emissiveMap: emissiveTexture,
envMap: envTexture,
reflectivity: 0.5
});MeshPhongMaterial
Specular highlights. Good for shiny, plastic-like surfaces.
const material = new THREE.MeshPhongMaterial({
color: 0x0000ff,
specular: 0xffffff, // Highlight color
shininess: 100, // Highlight sharpness (0-1000)
emissive: 0x000000,
flatShading: false, // Flat vs smooth shading
map: texture,
specularMap: specTexture, // Per-pixel shininess
normalMap: normalTexture,
normalScale: new THREE.Vector2(1, 1),
bumpMap: bumpTexture,
bumpScale: 1,
displacementMap: dispTexture,
displacementScale: 1
});MeshStandardMaterial (PBR)
Physically-based rendering. Recommended for realistic results.
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.5, // 0 = mirror, 1 = diffuse
metalness: 0.0, // 0 = dielectric, 1 = metal
// Textures
map: colorTexture, // Albedo/base color
roughnessMap: roughTexture, // Per-pixel roughness
metalnessMap: metalTexture, // Per-pixel metalness
normalMap: normalTexture, // Surface detail
normalScale: new THREE.Vector2(1, 1),
aoMap: aoTexture, // Ambient occlusion (uses uv2!)
aoMapIntensity: 1,
displacementMap: dispTexture, // Vertex displacement
displacementScale: 0.1,
displacementBias: 0,
// Emissive
emissive: 0x000000,
emissiveIntensity: 1,
emissiveMap: emissiveTexture,
// Environment
envMap: envTexture,
envMapIntensity: 1,
// Other
flatShading: false,
wireframe: false,
fog: true
});
// Note: aoMap requires second UV channel
geometry.setAttribute('uv2', geometry.attributes.uv);MeshPhysicalMaterial (Advanced PBR)
Extends MeshStandardMaterial with advanced features.
const material = new THREE.MeshPhysicalMaterial({
// All MeshStandardMaterial properties plus:
// Clearcoat (car paint, lacquer)
clearcoat: 1.0, // 0-1 clearcoat layer strength
clearcoatRoughness: 0.1,
clearcoatMap: ccTexture,
clearcoatRoughnessMap: ccrTexture,
clearcoatNormalMap: ccnTexture,
clearcoatNormalScale: new THREE.Vector2(1, 1),
// Transmission (glass, water)
transmission: 1.0, // 0 = opaque, 1 = fully transparent
transmissionMap: transTexture,
thickness: 0.5, // Volume thickness for refraction
thicknessMap: thickTexture,
attenuationDistance: 1, // Absorption distance
attenuationColor: new THREE.Color(0xffffff),
// Refraction
ior: 1.5, // Index of refraction (1-2.333)
// Sheen (fabric, velvet)
sheen: 1.0,
sheenRoughness: 0.5,
sheenColor: new THREE.Color(0xffffff),
sheenColorMap: sheenTexture,
sheenRoughnessMap: sheenRoughTexture,
// Iridescence (soap bubbles, oil slicks)
iridescence: 1.0,
iridescenceIOR: 1.3,
iridescenceThicknessRange: [100, 400],
iridescenceMap: iridTexture,
iridescenceThicknessMap: iridThickTexture,
// Anisotropy (brushed metal)
anisotropy: 1.0,
anisotropyRotation: 0,
anisotropyMap: anisoTexture,
// Specular
specularIntensity: 1,
specularColor: new THREE.Color(0xffffff),
specularIntensityMap: specIntTexture,
specularColorMap: specColorTexture
});Glass Material Example
const glass = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: 1,
thickness: 0.5,
ior: 1.5,
envMapIntensity: 1
});Car Paint Example
const carPaint = new THREE.MeshPhysicalMaterial({
color: 0xff0000,
metalness: 0.9,
roughness: 0.5,
clearcoat: 1,
clearcoatRoughness: 0.1
});MeshToonMaterial
Cel-shaded cartoon look.
const material = new THREE.MeshToonMaterial({
color: 0x00ff00,
gradientMap: gradientTexture // Optional: custom shading gradient
});
// Create step gradient texture
const colors = new Uint8Array([0, 128, 255]);
const gradientMap = new THREE.DataTexture(colors, 3, 1, THREE.RedFormat);
gradientMap.minFilter = THREE.NearestFilter;
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;MeshNormalMaterial
Visualize surface normals. Useful for debugging.
const material = new THREE.MeshNormalMaterial({
flatShading: false,
wireframe: false
});MeshDepthMaterial
Render depth values. Used for shadow maps, DOF effects.
const material = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking
});PointsMaterial
For point clouds.
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.1,
sizeAttenuation: true, // Scale with distance
map: pointTexture,
alphaMap: alphaTexture,
transparent: true,
alphaTest: 0.5, // Discard pixels below threshold
vertexColors: true // Use per-vertex colors
});
const points = new THREE.Points(geometry, material);LineBasicMaterial & LineDashedMaterial
// Solid lines
const lineMaterial = new THREE.LineBasicMaterial({
color: 0xffffff,
linewidth: 1, // Note: >1 only works on some systems
linecap: 'round',
linejoin: 'round'
});
// Dashed lines
const dashedMaterial = new THREE.LineDashedMaterial({
color: 0xffffff,
dashSize: 0.5,
gapSize: 0.25,
scale: 1
});
// Required for dashed lines
const line = new THREE.Line(geometry, dashedMaterial);
line.computeLineDistances();ShaderMaterial
Custom GLSL shaders with Three.js uniforms.
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color: { value: new THREE.Color(0xff0000) },
texture1: { value: texture }
},
vertexShader: `
varying vec2 vUv;
uniform float time;
void main() {
vUv = uv;
vec3 pos = position;
pos.z += sin(pos.x * 10.0 + time) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
uniform vec3 color;
uniform sampler2D texture1;
void main() {
// Use texture2D() for GLSL 1.0, texture() for GLSL 3.0 (glslVersion: THREE.GLSL3)
vec4 texColor = texture2D(texture1, vUv);
gl_FragColor = vec4(color * texColor.rgb, 1.0);
}
`,
transparent: true,
side: THREE.DoubleSide
});
// Update uniform in animation loop
material.uniforms.time.value = clock.getElapsedTime();Built-in Uniforms (auto-provided)
// Vertex shader
uniform mat4 modelMatrix; // Object to world
uniform mat4 modelViewMatrix; // Object to camera
uniform mat4 projectionMatrix; // Camera projection
uniform mat4 viewMatrix; // World to camera
uniform mat3 normalMatrix; // For transforming normals
uniform vec3 cameraPosition; // Camera world position
// Attributes
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;RawShaderMaterial
Full control - no built-in uniforms/attributes.
const material = new THREE.RawShaderMaterial({
uniforms: {
projectionMatrix: { value: camera.projectionMatrix },
modelViewMatrix: { value: new THREE.Matrix4() }
},
vertexShader: `
precision highp float;
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
});Common Material Properties
All materials share these base properties:
// Visibility
material.visible = true;
material.transparent = false;
material.opacity = 1.0;
material.alphaTest = 0; // Discard pixels with alpha < value
// Rendering
material.side = THREE.FrontSide; // FrontSide, BackSide, DoubleSide
material.depthTest = true;
material.depthWrite = true;
material.colorWrite = true;
// Blending
material.blending = THREE.NormalBlending;
// NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending
// Stencil
material.stencilWrite = false;
material.stencilFunc = THREE.AlwaysStencilFunc;
material.stencilRef = 0;
material.stencilMask = 0xff;
// Polygon offset (z-fighting fix)
material.polygonOffset = false;
material.polygonOffsetFactor = 0;
material.polygonOffsetUnits = 0;
// Misc
material.dithering = false;
material.toneMapped = true;Multiple Materials
// Assign different materials to geometry groups
const geometry = new THREE.BoxGeometry(1, 1, 1);
const materials = [
new THREE.MeshBasicMaterial({ color: 0xff0000 }), // right
new THREE.MeshBasicMaterial({ color: 0x00ff00 }), // left
new THREE.MeshBasicMaterial({ color: 0x0000ff }), // top
new THREE.MeshBasicMaterial({ color: 0xffff00 }), // bottom
new THREE.MeshBasicMaterial({ color: 0xff00ff }), // front
new THREE.MeshBasicMaterial({ color: 0x00ffff }) // back
];
const mesh = new THREE.Mesh(geometry, materials);
// Custom groups
geometry.clearGroups();
geometry.addGroup(0, 6, 0); // start, count, materialIndex
geometry.addGroup(6, 6, 1);Environment Maps
// Load cube texture
const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load([
'px.jpg',
'nx.jpg', // positive/negative X
'py.jpg',
'ny.jpg', // positive/negative Y
'pz.jpg',
'nz.jpg' // positive/negative Z
]);
// Apply to material
material.envMap = envMap;
material.envMapIntensity = 1;
// Or set as scene environment (affects all PBR materials)
scene.environment = envMap;
// HDR environment (recommended)
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
const rgbeLoader = new RGBELoader();
rgbeLoader.load('environment.hdr', texture => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
scene.background = texture;
});Material Cloning and Modification
// Clone material
const clone = material.clone();
clone.color.set(0x00ff00);
// Modify at runtime
material.color.set(0xff0000);
material.needsUpdate = true; // Only needed for some changes
// When needsUpdate is required:
// - Changing flat shading
// - Changing texture
// - Changing transparent
// - Custom shader code changesPerformance Tips
1. Reuse materials: Same material = batched draw calls 2. Avoid transparent when possible: Transparent materials require sorting 3. Use alphaTest instead of transparency: When applicable, faster 4. Choose simpler materials: Basic > Lambert > Phong > Standard > Physical 5. Limit active lights: Each light adds shader complexity
// Material pooling
const materialCache = new Map();
function getMaterial(color) {
const key = color.toString(16);
if (!materialCache.has(key)) {
materialCache.set(key, new THREE.MeshStandardMaterial({ color }));
}
return materialCache.get(key);
}
// Dispose when done
material.dispose();See Also
threejs-textures- Texture loading and configurationthreejs-shaders- Custom shader developmentthreejs-lighting- Light interaction with materials
Performance Optimization
Techniques for fast, smooth 3D experiences.
Instancing
Render many copies of same geometry efficiently:
// Instead of creating 10,000 individual meshes
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.InstancedMesh(geometry, material, 10000);
// Set transforms for each instance
const matrix = new THREE.Matrix4();
const position = new THREE.Vector3();
const rotation = new THREE.Euler();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3(1, 1, 1);
for (let i = 0; i < 10000; i++) {
position.set(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50);
rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI);
quaternion.setFromEuler(rotation);
matrix.compose(position, quaternion, scale);
mesh.setMatrixAt(i, matrix);
}
mesh.instanceMatrix.needsUpdate = true;
scene.add(mesh);
// Per-instance colors
mesh.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(10000 * 3), 3);
for (let i = 0; i < 10000; i++) {
mesh.setColorAt(i, new THREE.Color(Math.random(), Math.random(), Math.random()));
}Level of Detail (LOD)
Switch between detail levels based on distance:
const lod = new THREE.LOD();
// High detail (close)
const geometryHigh = new THREE.IcosahedronGeometry(10, 4);
const meshHigh = new THREE.Mesh(geometryHigh, material);
lod.addLevel(meshHigh, 0);
// Medium detail
const geometryMed = new THREE.IcosahedronGeometry(10, 2);
const meshMed = new THREE.Mesh(geometryMed, material);
lod.addLevel(meshMed, 50);
// Low detail (far)
const geometryLow = new THREE.IcosahedronGeometry(10, 0);
const meshLow = new THREE.Mesh(geometryLow, material);
lod.addLevel(meshLow, 100);
scene.add(lod);
// Update LOD in animation loop
function animate() {
lod.update(camera);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}Frustum Culling
Automatic - objects outside camera view aren't rendered.
// Force disable for specific object
object.frustumCulled = false;
// Manually test if in view
const frustum = new THREE.Frustum();
const cameraViewProjectionMatrix = new THREE.Matrix4();
cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);
if (frustum.intersectsObject(object)) {
// Object is visible
}Geometry Optimization
// Merge geometries (reduce draw calls)
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const geometries = [geom1, geom2, geom3];
const mergedGeometry = mergeGeometries(geometries);
const mesh = new THREE.Mesh(mergedGeometry, material);
// Dispose old geometries
geometries.forEach(g => g.dispose());
// Simplify geometry
import { SimplifyModifier } from 'three/addons/modifiers/SimplifyModifier.js';
const modifier = new SimplifyModifier();
const simplified = modifier.modify(geometry, Math.floor(geometry.attributes.position.count * 0.5));Texture Optimization
// Use appropriate sizes (power of 2)
// 512x512, 1024x1024, 2048x2048
// Compress textures
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
// Use mipmaps (auto-generated by default)
texture.generateMipmaps = true;
// Appropriate filtering
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;
// Anisotropic filtering (balance quality/performance)
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
// Dispose unused textures
texture.dispose();Material Sharing
// Share materials between meshes (reduce memory)
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh1 = new THREE.Mesh(geometry1, sharedMaterial);
const mesh2 = new THREE.Mesh(geometry2, sharedMaterial);
const mesh3 = new THREE.Mesh(geometry3, sharedMaterial);Shadow Optimization
// Reduce shadow map resolution
light.shadow.mapSize.width = 1024; // instead of 2048
light.shadow.mapSize.height = 1024;
// Limit shadow camera frustum
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 50; // only cast shadows within this range
light.shadow.camera.left = -10;
light.shadow.camera.right = 10;
// Use fewer shadow-casting objects
object.castShadow = false; // for distant/small objects
object.receiveShadow = false; // for objects that don't need shadows
// Cheaper shadow type
renderer.shadowMap.type = THREE.PCFShadowMap; // instead of PCFSoftShadowMapRender Target Optimization
// Lower resolution for post-processing
const renderTarget = new THREE.WebGLRenderTarget(
window.innerWidth * 0.5, // half resolution
window.innerHeight * 0.5
);
// Appropriate pixel format
renderTarget.texture.format = THREE.RGBAFormat;
renderTarget.texture.type = THREE.UnsignedByteType;
// Dispose when done
renderTarget.dispose();Object Pooling
// Reuse objects instead of creating/destroying
class ObjectPool {
constructor(factory, initialSize) {
this.factory = factory;
this.pool = [];
for (let i = 0; i < initialSize; i++) {
this.pool.push(factory());
}
}
get() {
return this.pool.length > 0 ? this.pool.pop() : this.factory();
}
release(obj) {
this.pool.push(obj);
}
}
const bulletPool = new ObjectPool(() => {
return new THREE.Mesh(bulletGeometry, bulletMaterial);
}, 100);
// Use
const bullet = bulletPool.get();
scene.add(bullet);
// Return when done
scene.remove(bullet);
bulletPool.release(bullet);Monitoring Performance
// FPS counter
const stats = new Stats();
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
// ... rendering
stats.end();
requestAnimationFrame(animate);
}
// Renderer info
console.log(renderer.info);
// Shows: geometries, textures, programs, calls, triangles, points, lines
// GPU timing
const query = renderer.extensions.get('EXT_disjoint_timer_query_webgl2');General Best Practices
- Limit draw calls (merge geometries, use instancing)
- Reduce polygon count (LOD, simplification)
- Optimize textures (compression, appropriate sizes)
- Share materials and geometries
- Use frustum culling
- Limit number of lights (3-5 max)
- Avoid transparent materials when possible
- Use object pooling for frequently created/destroyed objects
- Profile with browser DevTools
- Test on target devices
- Use WebGL 2 features when available