# LANE D — CITIZENS (populate the town) > **Prompt for Opus 4.8.** You are working in `/Users/jing/Documents/PROCITY`. Read > `docs/CITY_SPEC.md` and `docs/RESEARCH.md` first, then the source you are porting: > `90sDJsim/web/world/index.html` lines ~258–393 (`loadRig`, `spawnRig`, `_canon`, `_rotOnly`, > head-bone height normalization, `upgradeStreetPeople`) — this stack is proven, port it > faithfully. You own `web/js/citizens/*`, `web/citizens_test.html`, and `web/models/*`. > Do not touch other lanes' files. Plain ES modules, three.js from `web/vendor/`, randomness > via `core/prng.js`. Commit in small, described steps on `main` (GLBs are committed — keep > the set curated, not hoarded). ## Mission Streets that feel lived-in and shops with keepers, at city scale, on budget. Rigged characters near, cheap impostors far, deterministic per-citizen identity (same seed → same person walks the same beat), all built on the **house fleet rule**: shared base meshes + one canonical clip bank, never per-character rigs. ## Assets (copy, never edit locally) - Copy the ped fleet from `90sDJsim/web/world/models/peds/` (19 rigged GLBs + clip-only `walk.glb` / `idle.glb`) into `web/models/peds/`. Byte-identical copies — the canonical source stays upstream (house law; see `asset-pipeline` conventions). - Canonical hero rigs if needed: `~/Documents/character_kit/` (female + male). Clip bank: `~/Documents/mixamo-fetch/out/` (34 clips — sit, lean, look-around, phone etc. may exist; check before wanting new ones; **never re-download an existing clip**). New clips need John to run the mixamo-fetch flow — file wishes in `docs/LANES/LANE_D_NOTES.md`, don't block. - Licensing: 🟢 web-ok only. No ActorCore. ## Architecture - `web/js/citizens/rigs.js` — the ported stack: promise-cached `loadRig`; `spawnRig()` with SkeletonUtils.clone, `mixamorig\d+` canonicalization (one walk clip drives all 19 peds), `_rotOnly` position-track stripping, head-bone height normalization (seeded 1.55–1.95m), feet planted by min bone Y, random clip start offsets. Primitive-figure placeholders that hot-swap when GLBs arrive (`upgradeStreetPeople` pattern) — the town is populated from frame one. - `web/js/citizens/sim.js` — the citizen simulation (Vuntra's layered idea, hamlet-scale): - Deterministic roster per chunk: `rng(citySeed,'citizen',chunkKey+i)` → identity (ped index, height, walk speed, home/goal habits). Density by district (main street busy at midday, residential sparse, market square clumps). - **Near tier (<25m)**: full rig + mixer, walking footpath lanes (offset polylines along street edges from the CityPlan graph), turning at nodes with seeded choice, idle/loiter stops at shop windows and benches (bench-sit if a sit clip exists, else stand-loiter). - **Mid tier (25–70m)**: no mixers ticking — instanced/billboard impostors. Generate impostor sprites at runtime: render each ped GLB once to a small offscreen RT from 4 yaw angles → one sprite atlas → InstancedMesh quads picking the nearest angle. (This keeps impostors automatically in sync with the fleet; cache the atlas in-session.) - **Far**: culled entirely. Promotion/demotion by distance with hysteresis so nobody pops while you watch (swap only when off-screen or beyond a fade). - Time-of-day modulation from the shell's day segment (event or global): morning trickle, lunch rush, night = near-empty streets + the odd wanderer. - `web/js/citizens/keepers.js` — shopkeepers: one keeper per open shop, standing at the counter slot (Lane C exposes counter position in its `places` — coordinate via the event payload; until Lane C lands, test with a mock). Idle clip + a "greet" head-turn toward the player if a clip allows. Keepers are spawned when an interior builds, disposed with it. - Budgets: ≤ 24 rigged actives, ≤ 4 mixers updated per frame beyond the nearest 8 (stagger updates), impostor instancing = 1 draw call per atlas. All mixers pause when tab hidden. ## Test page (`web/citizens_test.html`) Standalone: a fixture street loop (hard-code a small street graph — do NOT import Lane A/B files; keep the fixture local), spawn slider 0–200 citizens, tier-visualization toggle (colour by tier), live HUD (rigged count, mixer ms, draw calls, fps), time-of-day slider, determinism check button (respawn roster, assert identical identities). ## Acceptance - 200-citizen fixture scene holds 60fps on an M-series MacBook; mixer update <2ms/frame. - No T-pose ever visible (placeholder → rig swap is seamless); no giants/ants (height normalization verified across all 19 peds); one shared walk clip animates every ped. - Same seed → same crowd, twice (assert). - Screenshots of near/mid tiers + a busy main-street shot into `docs/shots/laneD/`. - `docs/LANES/LANE_D_NOTES.md`: measured budgets + clip wishlist for the mixamo-fetch run.