Three.js Fundamentals
Use this skill for the rendering foundation of a general Three.js web experience.
For a playable game's system, lifecycle, or release work, use web-game-development;
for a narrowly scoped rendering concern, route to the matching threejs-* skill.
When to use this skill
- Set up or repair a scene, camera, renderer, canvas ownership, or animation loop
- Choose perspective versus orthographic projection or correct world/local transforms
- Make rendering responsive, color-managed, and safe on high-DPI displays
- Diagnose blank scenes, clipped content, wrong camera framing, or leaking GPU resources
Instructions
Step 1: Establish the project contract
- Read the installed
three version and existing renderer/canvas ownership before
changing imports or initialization.
- Keep one owner for the render loop and resize listener. Framework wrappers such as
React Three Fiber own those lifecycle concerns; do not add a competing raw loop.
- Confirm the render backend before using backend-specific APIs. This skill defaults to
WebGLRenderer; verify WebGPU patterns against the installed Three.js revision.
Step 2: Build the smallest visible scene
Use an explicit scene, camera, renderer, one lit mesh, and a render loop before layering
in loaders, shaders, or post-processing.
import * as THREE from "three";
const width = window.innerWidth;
const height = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 100);
camera.position.set(0, 1, 4);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshStandardMaterial({ color: 0x4f8cff }),
);
scene.add(new THREE.HemisphereLight(0xffffff, 0x334455, 2));
scene.add(mesh);
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
Use a Group to give a feature one transform root. Change position, quaternion, or
scale intentionally; local coordinates compose through parents, while world-space
queries require updateWorldMatrix when the scene has not rendered yet.
Step 3: Handle resize and disposal explicitly
function resize(width, height) {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
}
function disposeObject(root) {
root.traverse((object) => {
object.geometry?.dispose();
const materials = Array.isArray(object.material)
? object.material
: [object.material];
for (const material of materials) material?.dispose();
});
}
Dispose textures and render targets owned by the feature as well. Do not dispose shared
resources until every consumer is gone.
Step 4: Verify observable rendering behavior
- Confirm a visible mesh and stable camera framing at the intended canvas size.
- Resize through narrow, wide, and high-DPI cases; the drawing buffer must not stretch.
- Check the browser console for WebGL warnings and inspect
renderer.info only as a
diagnostic, not as a test oracle.
- Run the repository's build, typecheck, and relevant visual/browser test when present.
Decision guide
| Need |
Use |
| Scene graph, camera, renderer, transforms, lifecycle |
This skill |
| Custom vertices, instancing, or BufferGeometry |
threejs-geometry |
| PBR properties or mesh surface appearance |
threejs-materials |
| Lights, shadows, or image-based lighting |
threejs-lighting |
| Maps, UVs, HDR backgrounds, or render targets |
threejs-textures |
| Model/asset loading and progress |
threejs-loaders |
| AnimationMixer, clips, bones, or morphs |
threejs-animation |
| Raycasting, controls, picking, or input |
threejs-interaction |
| GLSL or material shader extension |
threejs-shaders |
| EffectComposer screen-space passes |
threejs-postprocessing |
Examples
Perspective product view
Use a PerspectiveCamera for a physically familiar object view. Set a deliberately
small near plane only when needed; an unnecessarily tiny near value wastes depth
precision and causes z-fighting.
Isometric-like board view
Use an OrthographicCamera when scale must remain constant across depth. Recalculate
left/right/top/bottom from aspect ratio on resize, then call updateProjectionMatrix().
Best practices
- Keep one
requestAnimationFrame owner per canvas.
- Clamp pixel ratio; unbounded device pixel ratio is a silent GPU-cost multiplier.
- Use
MeshStandardMaterial plus intentional lighting for normal PBR work instead of
compensating for an unlit scene with arbitrary color values.
- Keep camera clipping planes as tight as the scene permits.
- Pair every feature-owned GPU allocation with a teardown path.
References
1---2name: threejs-fundamentals3description: Build and debug the Three.js scene foundation: renderer setup, cameras, scene graph, transforms, resize handling, color management, render loops, and resource disposal. Use when creating a Three.js scene, choosing a camera or renderer, fixing coordinate space or hierarchy bugs, or establishing a reliable WebGL canvas baseline. Triggers on: three.js scene, WebGLRenderer, PerspectiveCamera, OrthographicCamera, Object3D, scene graph, camera setup, resize canvas, render loop, coordinate system, transform.4license: MIT5---67# Three.js Fundamentals89Use this skill for the rendering foundation of a **general Three.js web experience**.10For a playable game's system, lifecycle, or release work, use `web-game-development`;11for a narrowly scoped rendering concern, route to the matching `threejs-*` skill.1213## When to use this skill1415- Set up or repair a scene, camera, renderer, canvas ownership, or animation loop16- Choose perspective versus orthographic projection or correct world/local transforms17- Make rendering responsive, color-managed, and safe on high-DPI displays18- Diagnose blank scenes, clipped content, wrong camera framing, or leaking GPU resources1920## Instructions2122### Step 1: Establish the project contract23241. Read the installed `three` version and existing renderer/canvas ownership before25 changing imports or initialization.262. Keep one owner for the render loop and resize listener. Framework wrappers such as27 React Three Fiber own those lifecycle concerns; do not add a competing raw loop.283. Confirm the render backend before using backend-specific APIs. This skill defaults to29 `WebGLRenderer`; verify WebGPU patterns against the installed Three.js revision.3031### Step 2: Build the smallest visible scene3233Use an explicit scene, camera, renderer, one lit mesh, and a render loop before layering34in loaders, shaders, or post-processing.3536```js37import * as THREE from "three";3839const width = window.innerWidth;40const height = window.innerHeight;4142const scene = new THREE.Scene();43const camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 100);44camera.position.set(0, 1, 4);4546const renderer = new THREE.WebGLRenderer({ antialias: true });47renderer.setSize(width, height);48renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));49renderer.outputColorSpace = THREE.SRGBColorSpace;5051const mesh = new THREE.Mesh(52 new THREE.BoxGeometry(),53 new THREE.MeshStandardMaterial({ color: 0x4f8cff }),54);55scene.add(new THREE.HemisphereLight(0xffffff, 0x334455, 2));56scene.add(mesh);5758function render() {59 renderer.render(scene, camera);60 requestAnimationFrame(render);61}62render();63```6465Use a `Group` to give a feature one transform root. Change `position`, `quaternion`, or66`scale` intentionally; local coordinates compose through parents, while world-space67queries require `updateWorldMatrix` when the scene has not rendered yet.6869### Step 3: Handle resize and disposal explicitly7071```js72function resize(width, height) {73 camera.aspect = width / height;74 camera.updateProjectionMatrix();75 renderer.setSize(width, height, false);76}7778function disposeObject(root) {79 root.traverse((object) => {80 object.geometry?.dispose();81 const materials = Array.isArray(object.material)82 ? object.material83 : [object.material];84 for (const material of materials) material?.dispose();85 });86}87```8889Dispose textures and render targets owned by the feature as well. Do not dispose shared90resources until every consumer is gone.9192### Step 4: Verify observable rendering behavior9394- Confirm a visible mesh and stable camera framing at the intended canvas size.95- Resize through narrow, wide, and high-DPI cases; the drawing buffer must not stretch.96- Check the browser console for WebGL warnings and inspect `renderer.info` only as a97 diagnostic, not as a test oracle.98- Run the repository's build, typecheck, and relevant visual/browser test when present.99100## Decision guide101102| Need | Use |103|---|---|104| Scene graph, camera, renderer, transforms, lifecycle | This skill |105| Custom vertices, instancing, or BufferGeometry | `threejs-geometry` |106| PBR properties or mesh surface appearance | `threejs-materials` |107| Lights, shadows, or image-based lighting | `threejs-lighting` |108| Maps, UVs, HDR backgrounds, or render targets | `threejs-textures` |109| Model/asset loading and progress | `threejs-loaders` |110| AnimationMixer, clips, bones, or morphs | `threejs-animation` |111| Raycasting, controls, picking, or input | `threejs-interaction` |112| GLSL or material shader extension | `threejs-shaders` |113| EffectComposer screen-space passes | `threejs-postprocessing` |114115## Examples116117### Perspective product view118119Use a `PerspectiveCamera` for a physically familiar object view. Set a deliberately120small near plane only when needed; an unnecessarily tiny `near` value wastes depth121precision and causes z-fighting.122123### Isometric-like board view124125Use an `OrthographicCamera` when scale must remain constant across depth. Recalculate126left/right/top/bottom from aspect ratio on resize, then call `updateProjectionMatrix()`.127128## Best practices1291301. Keep one `requestAnimationFrame` owner per canvas.1312. Clamp pixel ratio; unbounded device pixel ratio is a silent GPU-cost multiplier.1323. Use `MeshStandardMaterial` plus intentional lighting for normal PBR work instead of133 compensating for an unlit scene with arbitrary color values.1344. Keep camera clipping planes as tight as the scene permits.1355. Pair every feature-owned GPU allocation with a teardown path.136137## References138139- [Three.js Fundamentals source coverage](https://github.com/CloudAI-X/threejs-skills/tree/main/skills/threejs-fundamentals)140- [Three.js documentation](https://threejs.org/docs/)141- [Three.js manual: creating a scene](https://threejs.org/manual/#en/creating-a-scene)