---
description: Three.js conventions
globs: ["src/**/*.{js,ts,jsx,tsx}"]
alwaysApply: false
---

# Three.js

- Import from `three` and addons from `three/addons/...` (ES modules). No global `THREE` or old `examples/js` files.
- One `WebGLRenderer` (`antialias: true`), set `renderer.setPixelRatio(Math.min(devicePixelRatio, 2))`, and handle resize by updating the camera aspect + `updateProjectionMatrix()`.
- Animate with `renderer.setAnimationLoop(tick)`; use a `THREE.Timer` or `Clock` delta for movement so speed is frame-rate independent.
- Color management: textures for color use `texture.colorSpace = THREE.SRGBColorSpace`. Keep the default output color space.
- Load models as glTF/GLB with `GLTFLoader` (+ `DRACOLoader`/`KTX2Loader` for compressed assets). Show loading progress via `LoadingManager`.
- Performance: reuse geometries and materials, use `InstancedMesh` for many copies, keep draw calls low, limit shadow-casting lights, and avoid creating objects (`new Vector3`) inside the loop; reuse scratch vectors.
- Dispose of what you remove: `geometry.dispose()`, `material.dispose()`, textures, and render targets.
- Keep game logic in plain classes/systems; the scene graph is for rendering.
- If the project uses React Three Fiber, follow its patterns (`useFrame`, declarative JSX) instead of imperative setup.
- WebGPU (`three/webgpu`) only if the project already opted in.

## Done when
No console errors, `renderer.info.render.calls` is reasonable for the scene, and `npm run build` passes.
