# Tower Crane Construction Site — one-shot task

You are working alone in the current directory, which is empty except for this file. Build the app described below **in this directory**, test it yourself, and finish with a short `README.md` (see section 10). Nobody will answer questions or give feedback while you work: make sensible engineering decisions and document them in code comments. When everything is done and verified, stop.

## ROLE

You are a senior real-time graphics engineer specializing in WebGL/Three.js. You write production-grade, self-contained, performant 3D web applications. You do not ask clarifying questions — you make sensible engineering decisions, document them in code comments, and deliver a complete working result in one pass.

## GOAL

Build a **3D construction-site simulator**: a tower crane builds a multi-storey residential building floor by floor. The user can scrub the construction timeline from floor 0 to floor N, toggle day/night lighting, and switch workers and supply vehicles on/off.

The result must run from a static file server (`python3 -m http.server`) with **zero errors in the console** and a **stable 60 FPS on a mid-range laptop GPU**. It will also be filmed for a video through the capture mode in section 12, so it has to look great, not just work.

---

## 1. TECH STACK (mandatory)

- **Three.js r16x+** loaded from a pinned CDN URL (`https://cdnjs.cloudflare.com/ajax/libs/three/<exact-version>/three.module.min.js`) via an **import map**, or via npm + Vite — pick one and be consistent.
- `OrbitControls` for camera navigation.
- **No external 3D model files (no .glb/.gltf/.fbx), no external textures, no image assets.** Every mesh is built procedurally from Three.js primitives (`BoxGeometry`, `CylinderGeometry`, `LatheGeometry`, `ExtrudeGeometry`, `TubeGeometry`, `Shape`) and every material/texture is either a flat PBR material or generated at runtime via `CanvasTexture`. This keeps the whole thing one-shot deliverable and CORS-free.
- **lil-gui** (or a hand-rolled HTML/CSS panel — your choice, but it must look clean, dark-themed, and be styled in the same file) for the control panel.
- Vanilla ES modules. **No React, no bundler-specific magic.** TypeScript is allowed only if you also ship the build config; otherwise plain JS.
- Post-processing is optional; if used, only `EffectComposer` + `UnrealBloomPass` for night mode, and it must be toggleable and off by default on low-end devices.

---

## 2. SCENE CONTENT

### 2.1 Site

- Ground plane ~120×120 units: asphalt/dirt with a procedural `CanvasTexture` (noise + subtle tiling), receiving shadows.
- Building foundation slab / pit with rebar hints.
- Site perimeter fence: repeating panel meshes (use `InstancedMesh`) with a gate.
- Details that sell the scene: material stockpiles (stacks of bricks / concrete panels / rebar bundles), a site trailer, portable toilets, a puddle or two, traffic cones, floodlight masts, a tire-wash zone.
- All static geometry that repeats **must** use `InstancedMesh`.

### 2.2 Tower Crane

Anatomy, modelled as a proper parent-child transform hierarchy so that animation is just rotations/translations of groups:

```
craneRoot (position on site)
└── mastGroup
    └── slewingUnit
        ├── jib
        │   └── trolley
        │       └── hookBlock
        │           └── hoistRope
        ├── counterJib
        └── cabin
```

Requirements:
- The mast is a **lattice**: build one section from thin boxes/cylinders (4 chords + diagonals) as a reusable geometry and instance/clone it vertically. Same approach for the jib and counter-jib trusses.
- **The mast grows with the building** (telescoping / self-climbing): as floors are added, mast height increases so the jib always clears the top floor by a safe margin. Animate this growth, do not snap it.
- Hoist rope must always connect trolley → hook and stretch/shrink correctly (recompute length + position each frame; do not scale a rope with visible texture stretch).
- Rotating red/white obstruction beacon on the jib tip, blinking at ~1 Hz, visible especially at night.
- Slight idle sway of the hook when moving (damped spring / lerp), so it doesn't look robotic.

### 2.3 Building

- Parametric generator: `buildFloor(index)` returns a `THREE.Group` for one storey.
- Each floor consists of: floor slab, columns, exterior walls with window openings, window glass (transparent material with a hint of environment color), balconies on some floors, interior partition hints.
- The **ground floor is different**: entrance group, larger glazing, canopy.
- The **roof (final floor)** gets: parapet, rooftop HVAC boxes, elevator machine room, antenna.
- Building footprint, floor height (~3.2 units) and floors count are constants at the top of the file.
- Floors under construction show intermediate states: **rebar cage → poured slab → columns → walls → glazing → finished**. Implement this as a per-floor `progress ∈ [0,1]` that controls sub-part visibility and material.
- Scaffolding wraps the top 2–3 completed floors and moves upward as construction advances. Use `InstancedMesh`.

### 2.4 Workers — toggleable

- 8–14 low-poly humanoids (capsule torso, sphere head, box limbs, hard hat cone/half-sphere). Hi-vis vest color varies per worker.
- Simple procedural walk cycle: sine-driven limb rotation, speed matched to translation (no foot sliding — tie stride phase to distance travelled).
- Behaviours (finite state machine): `walkToStockpile → carry → walkToBuilding → work(anim) → idle`. A few workers stand near the crane with raised arms guiding the load.
- Workers must live on the **current top floor** and on the ground, and their positions must update when the timeline scrubs.
- Toggle: `showWorkers` — hide the whole group and skip their update loop when off.

### 2.5 Vehicles — toggleable

Minimum set, all procedural low-poly:
1. **Concrete mixer truck** with rotating drum.
2. **Flatbed truck** delivering panels/rebar, with a cargo that visibly unloads.
3. **Wheel loader / excavator** with articulated boom (animated idle work loop).
4. Optional: **concrete pump** with articulated boom reaching the top floor.

Behaviour: vehicles spawn off-site, drive along a spline path (`CatmullRomCurve3`) through the gate, stop at the unloading zone, unload (animate, spawn a material pallet the crane will pick up), then drive off and loop. Wheels rotate proportional to travelled distance; the body steers along the curve tangent.

Toggle: `showVehicles`.

---

## 3. CRANE WORK CYCLE (core simulation)

Implement a state machine that runs one **lift cycle** and repeats:

```
IDLE → SLEW_TO_STOCKPILE → LOWER_HOOK → ATTACH_LOAD → RAISE_LOAD
     → SLEW_TO_BUILDING (+ TROLLEY_MOVE simultaneously) → POSITION → LOWER_LOAD
     → RELEASE (load becomes part of the floor under construction) → RETURN → IDLE
```

Rules:
- Slew, trolley and hoist move **simultaneously with different speeds**, like a real operator, not sequentially — but easing must make it readable.
- Every completed cycle advances the current floor's `progress` by a fixed increment. When `progress` reaches 1, the floor is finished, the crane mast climbs, and construction moves to the next floor.
- The load itself is visible: concrete bucket, rebar bundle, wall panel or window module — the type depends on the floor's current build stage.
- The load swings slightly (pendulum with damping) during slew.

---

## 4. LIGHTING & DAY/NIGHT

This is a headline feature. Implement a **continuous time-of-day system**, not a binary switch.

- `timeOfDay ∈ [0, 24)` hours, driving everything below. A GUI slider scrubs it; an `autoCycle` toggle advances it automatically at a configurable speed (`dayLengthSeconds`, default 120 s).
- Presets/buttons: **Dawn (6:00) · Day (12:00) · Sunset (19:00) · Night (23:00)**, animated transitions between them (tween ~1.5 s, easeInOutCubic), not instant jumps.

Driven by `timeOfDay`:

| Aspect | Requirement |
| --- | --- |
| Sun | `DirectionalLight` on a real arc: azimuth + elevation from the hour; goes below horizon at night. Color ramps warm-orange at dawn/dusk (`#ff9d5c`) → neutral white at noon (`#fff6e8`). Intensity follows a smooth curve, ~0 at night. |
| Moon | Second `DirectionalLight`, cold blue (`#7fa8ff`), low intensity, active only at night, opposite azimuth. |
| Ambient | `HemisphereLight` whose sky/ground colors and intensity lerp across the cycle — bright blue/tan by day, deep navy/near-black at night. |
| Sky | Gradient sky via a large inverted sphere with a custom `ShaderMaterial` (or `THREE.Sky`), colors driven by sun elevation. Stars fade in at night (`Points` with a small procedural sprite, opacity tied to darkness). |
| Fog | `FogExp2`, color matched to the horizon color of the current hour; slightly denser at dawn. |
| Shadows | `PCFSoftShadowMap`. **One** shadow-casting light at a time (sun by day, moon by night) — never two. Shadow camera frustum must be fitted tightly to the building + crane bounds and **updated as the building grows**, or shadows will visibly degrade. |
| Exposure | `renderer.toneMapping = ACESFilmicToneMapping`, `toneMappingExposure` animated with time of day. |

**Night mode must add artificial light:**
- Site floodlight masts: `SpotLight`s aimed at the building face, with visible emissive lamp housings. Cap the count (2–4 shadow-less spots + 1 shadowed) for performance.
- Crane jib beacon (blinking red) + white work lights on the crane cabin.
- Emissive windows: finished floors turn their windows into warm emissive material at night, with **randomized per-window on/off** and a couple that flicker. Day → windows go back to reflective glass.
- Vehicle headlights + tail lights on when dark.
- Optional bloom pass, on only at night, `strength` ramped by darkness.

**Performance rule:** all these lights must be created **once** at init and only have their `intensity`/`color`/`visible` mutated at runtime. Never create/dispose lights per frame — that triggers shader recompiles and stutters.

---

## 5. TIMELINE / SCRUB

- Slider `Construction progress` mapping `0.0 → totalFloors` with fractional values (e.g. `4.6` = floor 4 done, floor 5 at 60%).
- Scrubbing is **fully deterministic and instant**: `setConstructionState(progress)` rebuilds/toggles visibility of floors, scaffolding position, crane mast height, worker positions, load type. Dragging back and forth must produce identical results with **no memory growth** — do not spawn new meshes per scrub; pre-create all floors at init and toggle `visible` + per-floor `progress`.
- Play / Pause button plus a `buildSpeed` multiplier (0.25× … 4×) that runs the real animated crane cycle forward.
- `Reset to 0` and `Jump to finished` buttons.
- Timeline readout: `Floor 5 / 10 · 62% · Day 3, 14:20`.

---

## 6. CONTROL PANEL (UI)

Grouped, collapsible, dark, top-right, non-blocking, and it must not steal camera drag events:

**Construction**
- `progress` slider (0 → N floors), Play/Pause, `buildSpeed`, Reset, Jump to end
- `totalFloors` (5 / 10 / 15 / 20) — rebuilds the building
- `showScaffolding`

**Time & Light**
- `timeOfDay` slider (0–24), `autoCycle`, `dayLengthSeconds`
- Presets: Dawn / Day / Sunset / Night
- `shadowsEnabled`, `shadowQuality` (Low 1024 / Med 2048 / High 4096)
- `bloom` (night only), `exposure`

**Entities**
- `showWorkers` + `workerCount`
- `showVehicles`
- `showCrane`, `craneAutoOperate`
- `showHelpers` (grid, axes, light gizmos, path debug lines) — off by default

**Camera**
- Presets: `Orbit (free)` · `Crane cab (from the cabin)` · `Hook cam (looking down from the hook)` · `Aerial` · `Street level`
- `cinematic` toggle: slow automatic orbit around the site
- Smooth animated transition between camera presets (lerp position + quaternion, ~1.2 s)

**Stats:** FPS, draw calls, triangles (`renderer.info`) in a small readout.

---

## 7. INTERACTION

- OrbitControls with `enableDamping`, sane min/max polar angle (never below ground) and zoom limits.
- Keyboard: `Space` play/pause · `N` night · `D` day · `←/→` step one floor · `1–5` camera presets · `H` hide/show UI.
- Hover highlight (raycast) on crane / building / vehicles with a small tooltip label ("Tower crane · Liebherr-style · max lift 8 t", "Floor 7 — glazing 45%").
- Click a floor → camera flies to it and the timeline jumps to that floor.
- Responsive: handle `resize`, clamp `devicePixelRatio` to ≤ 2.

---

## 8. CODE QUALITY REQUIREMENTS

- `index.html` + `main.js` + `style.css` (max 3 files) in this directory; `main.js` may be one file organized with clear `// ==== SECTION ====` banners.
- Top-of-file `CONFIG` object holding every tunable constant (floor height, footprint, crane speeds, colors, light intensities, timings). **No magic numbers scattered in the code.**
- Clear module-ish structure: `createSite()`, `createCrane()`, `createBuilding()`, `createWorkers()`, `createVehicles()`, `LightingSystem`, `TimelineController`, `CraneController`, `CameraDirector`, `UI`.
- One `requestAnimationFrame` loop with a **fixed-step accumulator** for simulation and delta-time interpolation for rendering — animation must not depend on framerate.
- Reuse `Vector3`/`Quaternion`/`Matrix4` scratch objects; **zero allocations in the hot loop**.
- Share geometries and materials aggressively (one material per visual class). Provide a `dispose()` path used when `totalFloors` changes so rebuilds don't leak.
- Frustum culling on, `matrixAutoUpdate = false` for static objects.
- JSDoc on every public function. Comments in English.
- Graceful degradation: detect low FPS (rolling average < 40 for 2 s) and auto-drop shadow resolution / disable bloom once, logging it to console.

---

## 9. VISUAL QUALITY BAR

Aim for "clean stylized realism" — not photoreal, not toy. Reference feel: an architectural-viz explainer video.
- Cohesive palette: warm concrete greys, crane yellow `#f5c518`, hi-vis orange `#ff6a13`, deep blue night.
- `MeshStandardMaterial` everywhere with sensible `roughness`/`metalness`; no flat `MeshBasicMaterial` except the sky, stars and emissive lamps.
- Subtle contact shadows / AO look via a darkened ground blob under large objects.
- Anti-aliasing on, `outputColorSpace = SRGBColorSpace`.
- No z-fighting, no visible geometry interpenetration on the crane, no rope detached from the hook.

---

## 10. DELIVERABLE & SELF-CHECK

Write the **complete, runnable code** to files in this directory — no placeholders, no `// TODO`, no "implement this yourself", no truncation.

Test it yourself before you finish: serve the folder, open it in a browser (headless Chrome is installed at the default macOS path if you want to automate this), check the console, and exercise the capture mode from section 12.

Finish with `README.md`: **How to run** and **Self-check**, where you honestly mark each item:

- [ ] Opens and runs with zero console errors
- [ ] Timeline scrubs 0 → N and back with identical, deterministic results
- [ ] Day/night is continuous, with animated preset transitions, and night has floodlights + emissive windows + beacon
- [ ] Crane mast grows with the building; rope always connects trolley → hook
- [ ] Workers and vehicles toggle cleanly on/off, including their update loops
- [ ] Camera presets transition smoothly; hook cam and cab cam actually work
- [ ] 60 FPS on a mid-range GPU; draw calls stay under ~300 thanks to instancing
- [ ] No allocations in the animation loop; changing `totalFloors` disposes the old building
- [ ] Capture mode (section 12) works: same inputs → same frame, `?capture=1` hides all UI
- [ ] None of the known failures from section 13 happen

If any box cannot be ticked, fix the code before finishing. If you still cannot, say so in the README instead of ticking it.

---

## 11. EXPLICIT NON-GOALS

Do not add: physics engines (Rapier/Ammo/Cannon), multiplayer, backends, model loaders, external textures, sound, React/Vue, or a build step beyond an optional Vite dev server. Everything is procedural, client-side, and self-contained.

---

## 12. CAPTURE MODE (the app will be filmed)

The finished app will be filmed frame by frame by a script, so the same shot can be taken of every build. Implement this API exactly:

- Opening `index.html?capture=1` enters capture mode: canvas is exactly **1920×1080** CSS pixels at `devicePixelRatio` 1, all UI/overlays/readouts hidden, `autoCycle` off, adaptive quality disabled, and **the app does not advance anything on its own** (no wall-clock time, no `requestAnimationFrame`-driven simulation). Bloom and shadows follow the state set below.
- `window.capture` must exist in capture mode with these methods:

```js
await window.capture.ready();            // resolves when the scene is built and all assets are compiled
window.capture.bounds();                 // → { center:[x,y,z], size:[x,y,z], buildingTop:y, craneTop:y,
                                         //     cab:[x,y,z], hook:[x,y,z] }  world units, for the current state
window.capture.set({
  progress: 4.6,                         // construction progress, same meaning as the timeline slider
  hour: 22.5,                            // time of day 0–24
  floors: 10,                            // totalFloors (optional, rebuilds if changed)
  workers: true, vehicles: true,         // optional toggles
  bloom: true, shadows: 'high'           // optional quality ('low' | 'med' | 'high')
});
window.capture.step(1/60);               // advance the simulation (crane cycle, vehicles, workers, beacon,
                                         // hook sway…) by exactly dt seconds, deterministically
window.capture.camera({ position:[x,y,z], target:[x,y,z], fov: 45 });  // or { preset: 'cab' | 'hook' | 'aerial' | 'street' | 'orbit' }
window.capture.render();                 // render one frame synchronously with the current state
```

- Determinism: after `set(...)` with the same arguments and the same sequence of `step()` calls, the rendered frames must be identical (seed any randomness).
- Outside capture mode the app behaves normally (UI, real-time loop).

## 13. KNOWN FAILURES FROM EARLIER RUNS (avoid them)

Earlier attempts at this task broke in these ways. Check for each one:

- Night scene ran at ~13 FPS: too many shadowed lights and non-instanced meshes (600+ draw calls). Keep one shadow-casting light, instance everything repeated, stay under ~300 draw calls.
- Changing the time of day silently turned shadows back on after the user disabled them.
- Vehicles parked to unload and never drove off again.
- The crane mast did not climb when floors were added, so the jib ended up inside the building.
- The progress slider range did not follow `totalFloors` after a rebuild.
- The scaffolding toggle was lost after changing `totalFloors`.
- Under headless software rendering FPS is in single digits — that is the CPU renderer, not your app; judge performance with a real GPU or by draw calls.

### Optional stretch goals (implement only if the core is complete and stable)

- Weather: rain particles + wet-ground roughness change, wind affecting the hook swing and crane weathervaning at idle.
- Blueprint/x-ray view mode (wireframe + section cut through the building).
- Mini-map with crane slew angle indicator.
- Export the current frame as PNG.
