<!-- skill: Three.js Scenes | version: 1.0.0 | description: Three.js renderer, GLTF loads, controls and perf discipline | category: Web -->
## Instructions
- Boot: `WebGLRenderer({antialias:true})` + `outputColorSpace = SRGBColorSpace` + `toneMapping = ACESFilmic` + `shadowMap.enabled = PCFSoft`; `PerspectiveCamera(45, w/h, 0.1, 100)`; `Scene.background` + fog for depth.
- Lights: `HemisphereLight(sky, ground, 0.6)` base + `DirectionalLight(2.2)` key with `shadow.mapSize 2048` + tuned frustum + `AmbientLight` fill 0.2; never 6 unshadowed point lights (perf cliff).
- Loaders: `GLTFLoader` + `DRACOLoader` (`setDecoderPath('/draco/')`) for compressed meshes, `TextureLoader` with `colorSpace = SRGBColorSpace` for albedo; `LoadingManager` progress bar + error toast.
- Controls: `OrbitControls(camera, renderer.domElement)` with `enableDamping`, `target.set()`, `min/maxDistance` + `maxPolarAngle` clamps; `controls.update()` in loop; touch `one-finger rotate` default.
- Animation loop: `renderer.setAnimationLoop(tick)` with `clock.getDelta()` capped 0.05, `requestAnimationFrame` delta-driven (not fixed 60 assumptions); pause when `document.hidden` or offscreen via IntersectionObserver.
- Resize/HDPR: `setPixelRatio(Math.min(devicePixelRatio, 2))` (1.5 on mobile), `setSize(w, h, false)` + camera aspect update on ResizeObserver; `powerPreference: 'high-performance'`; test 4K + Moto G.
- Materials: `MeshStandardMaterial` (`roughness/metalness` maps) default, `MeshPhysicalMaterial` only for glass/clearcoat hero; reuse geometries/materials, `texture.anisotropy = renderer.capabilities.getMaxAnisotropy()` capped 8.
- Instancing perf: `InstancedMesh(count)` for 100+ repeats (trees/bolts), merged `BufferGeometryUtils.mergeGeometries` for static sets; `frustumCulled = true`, LOD `THREE.LOD` tiers for hero meshes.
- Shadows: only key light casts, `castShadow` hero meshes + `receiveShadow` ground, `bias: -0.0005` acne fix; 2048 map desktop / 1024 mobile; disable shadows entirely on low-tier (`?lite` flag).
- Post/debug: `Stats` FPS + `renderer.info.render.calls/triangles` HUD in dev, `Spector.js` draw-call audit; `ACES + antialias` enough — skip EffectComposer bloom unless hero demands it (half-res).
- Cleanup: `geometry.dispose()/material.dispose()/texture.dispose()` on unmount/route change, `controls.dispose()`, `renderer.dispose()` + `cancelAnimationLoop`; no per-frame `new Vector3` (reuse temps).
- Framework: `react-three-fiber` (`<Canvas shadows dpr={[1,2]} camera={{...}}>` + `useFrame` + `useGLTF.preload`) or vanilla module; `Suspense` fallback loader; `useTexture` cached.
## Examples
- Product viewer: GLTF sneaker + turntable `autoRotate`, HDRI `Environment`, color swatches swap `material.color`, zoom clamps, 60fps via 1.5 HDPR mobile.
- Showroom: 200 instanced chairs + merged walls, 1 shadow light, LOD statues, fog + ACES, loading bar via `LoadingManager`, lite flag disables shadows.
- Data viz: 5k instanced bars (`InstancedMesh` + per-instance color), OrbitControls pan limits, raycast hover tooltip throttled, resize/HDPR discipline.
## Workflows
1. Scaffold: Vite + `three @types/three`, renderer/camera/lights/ground baseline, `Stats` + `renderer.info` HUD on.
2. Load: GLTF+DRACO + SRGB textures, LoadingManager progress, Environment HDRI, materials reuse audit.
3. Interact: OrbitControls clamps + damping, raycast UI, autoRotate hero, pause offscreen/hidden.
4. Perf: HDPR cap + resize observer, instancing/merge/LOD, shadow single-light tune, draw-call audit <100.
5. Ship: dispose on unmount, lite fallback, 4K + mobile FPS verified, preload hero model.
## Templates
- Baseline scene + controls + resize:
```ts
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: 'high-performance' });
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
const scene = new THREE.Scene(); scene.background = new THREE.Color(0x0b0d12);
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100); camera.position.set(3, 2, 5);
scene.add(new THREE.HemisphereLight(0xffffff, 0x223344, 0.6));
const key = new THREE.DirectionalLight(0xffffff, 2.2);
key.castShadow = true; key.shadow.mapSize.set(2048, 2048); key.shadow.bias = -0.0005;
scene.add(key);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; controls.minDistance = 2; controls.maxDistance = 12;
controls.maxPolarAngle = Math.PI / 2.1;
new ResizeObserver(() => {
  const w = innerWidth, h = innerHeight;
  renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix();
}).observe(document.body);
const draco = new DRACOLoader().setDecoderPath('/draco/');
const gltf = await new GLTFLoader().setDRACOLoader(draco).loadAsync('/shoe.glb');
gltf.scene.traverse((o: any) => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } });
scene.add(gltf.scene);
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
  const dt = Math.min(clock.getDelta(), 0.05);
  controls.update(); renderer.render(scene, camera);
});
```
- Instancing + R3F:
```ts
const geo = new THREE.BoxGeometry(0.2, 1, 0.2);
const mat = new THREE.MeshStandardMaterial({ roughness: 0.6 });
const mesh = new THREE.InstancedMesh(geo, mat, 1000);
const m = new THREE.Matrix4();
for (let i = 0; i < 1000; i++) { m.setPosition((i % 32) * 0.5, 0, Math.floor(i / 32) * 0.5); mesh.setMatrixAt(i, m); }
scene.add(mesh);
```
```tsx
import { Canvas, useFrame } from '@react-three/fiber';
import { useGLTF, Environment, OrbitControls } from '@react-three/drei';
function Shoe() { const { scene } = useGLTF('/shoe.glb');
  useFrame((_, dt) => { scene.rotation.y += dt * 0.3; }); return <primitive object={scene} />; }
<Canvas shadows dpr={[1, 2]} camera={{ position: [3, 2, 5], fov: 45 }}>
  <Shoe /><Environment preset="city" /><OrbitControls enableDamping /></Canvas>
```
## Tools
- SARAH shell_run for `vite dev/build`, `gltf-transform inspect/optimize`, Spector draw-call capture, FPS HUD.
- SARAH file_write for scene/loaders/controls/components; web_fetch for Three.js + R3F/Drei docs.
## Advanced
- Optimization: `gltf-transform optimize --compress draco --texture-compress webp` (70% smaller), KTX2/Basis universal textures, `meshopt` filters; `LOD` 3 tiers + `OcclusionCulling` rooms.
- Shaders: `onBeforeCompile` wind/outline tweaks, `InstancedBufferAttribute` per-instance phase, half-float RT for bloom chain (`UnrealBloomPass` half-res + `OutputPass` last); profile Mali/Adreno separately.
- Ops: `?lite` (no shadows/HDPR 1), `matchMedia('(prefers-reduced-motion)')` stops autorotate, Sentry `renderer.info` breadcrumbs on crash, CDN `model.glb` + `Accept-Encoding: br` + immutable hash.
## Documentation
- https://threejs.org/docs/
- https://threejs.org/manual/
- https://docs.pmnd.rs/react-three-fiber/getting-started/introduction
- https://github.com/pmndrs/drei
- https://gltf-transform.dev/
