calesthio/threejs-scene-composition
Production guidance for planning, building, animating, capturing, and reviewing complete Three.js scenes for rendered media. Use for browser-native 3D product shots, title sequences, procedural worlds, glTF scene assembly, camera and lighting animation, shaders, post-processing, deterministic frame export, and Three.js render QA; not for modeling standalone assets, WebXR interaction, or generic website decoration.
npx skills add https://github.com/calesthio/generative-media-skills --skill threejs-scene-composition
Use Three.js when depth, camera, lighting, material response, geometry, or spatial animation carries the media idea and a browser-rendered scene is an appropriate production surface. This skill begins after asset requirements and creative intent are known and ends with a reproducible rendered scene package.
It does not teach mesh modeling, retopology, rig authoring, physics simulation, WebXR product design, or game logic. It may consume approved glTF assets, animation clips, textures, environment maps, fonts, data, and audio cues produced elsewhere.
Three.js releases frequently and examples/addons may change paths or APIs. Facts and links were checked 2026-07-12. Pin and record the installed revision, check migration notes, and test the exact target browser and renderer before delivery. WebGPU support and renderer parity are especially volatile.
Use Three.js when the deliverable needs:
Prefer another approach when:
Do not add a 3D scene merely to make a composition feel expensive. The camera and dimensionality must serve the message.
Before implementation, define:
Create a scene manifest mapping stable IDs to files, checksums, transformations, licenses, and scene usage. Freeze remote assets locally before final rendering.
Documented fact: Three.js composes an Object3D hierarchy in a Scene, views it through a camera, and draws it through a renderer. Child transforms inherit parent transforms. Perspective and orthographic cameras serve different spatial readings.
Structure the scene by production role:
scene
+- world
| +- environment
| +- set
| `- practical-lights
+- subject-root
| +- approved-model
| `- subject-controls
+- fx-root
+- camera-rig
| +- camera-target
| `- camera
`- review-guides
Use stable names and keep camera rigs, subject transforms, imported animation, and procedural effects separate. Avoid modifying deeply nested imported nodes by array index; resolve named nodes once and validate that they exist.
Documented fact: glTF 2.0 describes scenes, nodes, meshes, skins, animations, cameras, PBR materials, textures, and binary resources. Three.js GLTFLoader returns loaded scenes and animation clips and supports a documented set of glTF extensions. Core glTF materials use metallic-roughness PBR.
For every imported asset:
Use Draco or mesh compression and KTX2/Basis texture paths only after checking decoder deployment, target support, visual changes, and startup cost. Compression is a delivery decision, not an automatic quality improvement.
Documented fact: Three.js uses a color-management workflow with Linear-sRGB as its working color space. Color textures such as base color and emissive maps require an sRGB color-space annotation; non-color data textures such as normal, roughness, and metalness maps should not be treated as color. Output conversion and tone mapping are renderer concerns.
Color-critical workflow:
Use physically based materials when lighting response matters. Preserve the intended distinction between dielectrics and metals, and review roughness, normals, clearcoat, transmission, volume, sheen, iridescence, and other extensions only where the asset and target support them.
Lighting plan:
Production heuristic: begin with the fewest lights and shadow maps that produce the intended form. Add complexity in response to a visible deficiency, not because more lights sound cinematic.
Three.js can combine imported clips, scripted transforms, morph targets, cameras, and shader uniforms. Keep one declared owner per property.
Documented fact: AnimationMixer controls AnimationAction instances created from AnimationClip data. Clips contain keyframe tracks. Actions can loop, crossfade, change weight, and use normal or additive blending.
Use morphs for approved shape changes, facial poses, or corrective deformation. Validate that topology and target order match. Clamp or intentionally combine influence values; inspect midpoints for volume loss, self-intersection, and texture stretching.
Write procedural state as a pure function of composition time whenever possible:
function applyFrameState(frame, fps) {
const time = frame / fps;
subject.rotation.y = baseRotation + time * radiansPerSecond;
material.uniforms.uTime.value = time;
cameraRig.position.copy(cameraPath.getPointAt(clamp01(time / shotDuration)));
}
This example assumes clamp01(value) returns Math.max(0, Math.min(1, value)) and cameraPath is a configured Three.js curve such as a Curve3 implementation. Validate the actual curve and camera target separately.
Seed particle distributions and noise inputs. Do not accumulate state with position.x += ... during frame export because seeking backward or rendering frames out of order will produce the wrong result.
Live preview commonly uses requestAnimationFrame and incremental deltas. Fixed media capture should instead derive the entire scene state from the requested frame.
For each frame:
time = frame / fps from the composition contract.update(delta) calls.Example adapter shape:
export function renderAtFrame(frame, fps) {
const time = frame / fps;
mixer.setTime(time);
applyFrameState(frame, fps);
scene.updateMatrixWorld(true);
composer.render();
}
Check the current AnimationMixer semantics before using setTime() with non-unit mixer time scale. The principle is absolute-time evaluation, not allegiance to one API call.
Do not use wall-clock time, unseeded randomness, live network responses, hidden-tab timing, device orientation, or unconstrained physics during final capture. Bake simulations or define a reproducible fixed-step cache when simulation is essential.
Render the same stress frames in two clean runs and compare them. Exact GPU pixels may vary across backends or hardware; define tolerances and lock the render environment when exact reproducibility is required.
Use built-in materials first when they meet the visual requirement. Custom ShaderMaterial, raw shaders, node-based shading, and post-processing increase both creative range and portability risk.
For custom shaders:
For post-processing:
Documented fact: removing a mesh from a scene does not automatically release its geometry, material, texture, or render-target GPU resources. Disposable resources expose dispose() methods. Three.js provides renderer memory information for diagnostic use.
Production approach:
Measure a representative sequence, not an empty scene. Record load time, first-frame shader compilation, peak memory, render time per frame, readback/capture cost, and encoded output quality.
Documented fact: WCAG 2.2 SC 2.3.1 limits content to no more than three flashes in any one-second period unless below general and red flash thresholds. Evaluate loops while looping and test at the largest intended presentation.
Also review:
For interactive publication, expose motion controls and honor user preferences where applicable. For prerecorded video, produce a separate calmer output because a media query cannot alter already rendered pixels.
Three.js is MIT-licensed, but the scene's dependencies retain their own terms. Record licenses and permissions for models, textures, HDRIs, fonts, shaders, motion clips, mocap, plugins, logos, and reference media. Generated ingredients need provider/model/version/prompt/seed metadata when available.
Do not assume an asset downloaded from a public demo is cleared for commercial reuse. Preserve required notices. If a model depicts a real person, branded product, private location, artwork, or protected character, verify the applicable consent and rights before rendering.
Review these gates:
Handoff should include source code, pinned dependencies/lockfile, local assets, scene manifest, render settings, exact command or host entrypoint, representative frame checks, performance notes, provenance ledger, license notices, QA report, and known backend limitations.
This is a complete example, not a mandatory formula.
Intent: produce a 12-second 4K orbit of an approved running shoe for a product launch. Material and color fidelity matter more than dramatic effects.
Inputs and constraints: validated GLB; approved base-color swatches; two 4K texture sets; licensed studio HDRI; 3840x2160 at 30 fps; H.264 review plus high-quality master frames; no invented logos or geometry.
Workflow:
Expected result: stable geometry and logo, repeatable camera frames, neutral material response, readable contact, and approved color through the final encode.
Likely failures: double color conversion, roughness map tagged as sRGB, environment overpowering approved color, near-plane clipping, shader compilation hitch on frame one, and compression damaging knit texture.
Variation: for 9:16, author a closer camera path and different final hold rather than cropping the 16:9 orbit.
This is a complete example, not a mandatory formula.
Intent: make an eight-second title sequence in which 2,000 instanced columns rise into a city-like word silhouette while a camera passes through it.
Inputs and constraints: 1920x1080 at 30 fps; one approved title; no external 3D assets; deterministic seed; restrained glow; reduced-motion alternative; no flashing.
Workflow:
Expected result: identical instance layout across renders, no one-frame jumps, readable title, stable camera, and bounded GPU memory.
Likely failures: unseeded instance placement, accumulated transforms breaking backward seek, moire in dense columns, excessive bloom, camera clipping, and no readable hold.
Variation: if GPU or render-worker limits are tight, pre-render the 3D layer with alpha and composite it in a 2D runtime, retaining the scene manifest and provenance.
Official and authoritative sources checked 2026-07-12:
GLTFLoader: https://threejs.org/docs/#examples/en/loaders/GLTFLoaderTake calesthio/threejs-scene-composition from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.