# Task: a flyable, enterable, film-quality bush seaplane for a Three.js first-person game
You are building a **self-contained module** that will later be dropped into an existing browser game ("FarCry Lagoon": a first-person tropical island game in Three.js). You do **not** have access to the game's code. Another engineer (who knows the game) will integrate your module, so your job is to deliver:
1. **The seaplane module** — model, cockpit interior, flight + water physics, controls, interactions, sound, effects — written to the strict technical contract below so it can be pasted into the game with minimal glue.
2. **A sandbox page** that shows it off and lets you test everything: a small island, open sea with waves, a wooden pier, a first-person walker, and a chase/free camera. The sandbox is a test bench only; it will not be integrated.
3. **A short `INTEGRATION.md`** describing the public API, the coordinate frame, the env callbacks, keymap and anything the integrator must know.
The reference image is attached (`seaplane_ref.webp`). Save it in the repo as `reference/seaplane_ref.webp` and keep comparing your renders against it. Quality bar: it should look like a hero vehicle from Far Cry / Just Cause, not a programmer-art prop. Take your time — correctness and visual quality matter more than speed. Work in iterations: build → screenshot from several angles → compare with the reference → fix.
---
## 1. Hard technical contract (non-negotiable — integration depends on it)
- **Three.js r169 exactly** (`three@0.169.0`) as ES modules from the CDN via importmap:
```html
<script type="importmap">{"imports":{"three":"https://t.co/SI0sYiA81k","three/addons/":"https://t.co/Sajx3j9wlL"}}</script>
```
No npm runtime dependencies, no bundler, no TypeScript, no Vite. Plain `.js` ES modules that run from any static server (e.g. `npx serve` / a tiny node server).
- **Module syntax restrictions** (the game has a custom bundler that inlines modules):
- Only `export function name`, `export class Name`, `export const/let name = ...`. **No** `export default`, **no** `export { a, b }` lists, **no** re-exports.
- **No** default imports (`import X from`), only `import * as THREE from 'three'` and named imports `import { A } from '...'`.
- **No** dynamic `import()`, **no** `import.meta`, **no** top-level `await`, **no** circular imports between your files.
- Allowed externals: `three` and `three/addons/...` only (e.g. `BufferGeometryUtils`, `RoundedBoxGeometry` are fine).
- **Zero external assets.** No downloaded textures, models, HDRIs, fonts or sound files. Everything is procedural:
- geometry built in code (lathe / extrude / tube / custom BufferGeometry / CSG-like manual modelling),
- textures generated at startup on canvas (`CanvasTexture`, `DataTexture`) or in shaders, deterministically from a seed,
- sound synthesized with WebAudio.
- **Never patch `THREE.ShaderChunk` globally** and don't replace three's lighting/fog/shadow chunks — the game already patches them (cascaded shadows, custom fog). Use stock `MeshStandardMaterial` / `MeshPhysicalMaterial`; if you need custom shading, use `onBeforeCompile` locally on your own materials only, with a unique `customProgramCacheKey`, and keep `fog: true` working.
- **No real lights added at runtime.** Changing the number of lights recompiles every shader in the game (a visible freeze). Nav lights, instrument backlight, landing light, exhaust glow = emissive materials + additive sprites / light cones / a projected fake light pool on the water. Optionally ONE `SpotLight` for the landing light, created once at construction (intensity 0 when off, never shadow-casting), behind an option `realLandingLight: false` by default.
- **No mid-game shader compiles.** Every material / define variant the plane can ever use must exist from the start. Don't toggle `transparent`, `defines`, `vertexColors`, `side` etc. at runtime; switch visibility or uniforms instead. Provide `plane.prewarm(renderer, camera)` that compiles all programs (incl. LODs, interior, broken parts, effects) up front.
- **Units / frame:** metres, kilograms, seconds, radians, **+Y up**, sea level **y = 0**. Plane local frame: **nose toward −Z, right wing toward +X, up +Y**, origin at the centre of gravity at rest. Document it in `INTEGRATION.md`.
- **Don't touch globals:** no `window` listeners inside the module except through an explicit `attachInput(domElement)` / `detachInput()` pair; no pointer-lock requests in the module (the game owns pointer lock; the sandbox may request it itself); no `requestAnimationFrame` in the module (the host calls `update(dt)`); no `renderer.setAnimationLoop`, no changing renderer settings.
- **Desktop only**, keyboard + mouse. No touch / mobile / gamepad code.
- **Performance budget** (the game is already GPU-heavy; target 60 FPS at 1080p on a mid-range GPU):
- LOD0 (camera ≤ ~30 m): exterior ≤ ~120k triangles and ≤ ~30 draw calls; interior ≤ ~80k…