Game Design Document · v0.1 · Build target: Claude Code · Engine: three.js + vanilla DOM
Night·Shift
A noir dispatcher game. Calls come in over the wire. You decide who goes where, with what, and what it'll cost.
01 The pitch
You're a graveyard-shift dispatcher at a small PI agency.
It's always raining. The radio is always alive. Your operatives are good at three things each, and tonight you'll need them all. A clean shift pays well; a bad one brings heat from people you don't want to know.
The fantasy is quiet competence. The player is not the action hero — they're the calm voice on the radio choosing the right hammer for the right nail under time pressure. Every call is a tiny matching puzzle: requirements vs. roster vs. travel time vs. heat. Lose a few in a row and the night starts to spiral.
North-star feeling
The moment a player thinks "I should send Doc and have Pell back her up if Crowe doesn't make it back in time." That's the game. Optimization with personality.
02 Core loop
- Call arrives. Radio chatter, beacon pulses on the map, card slides into the queue with a code, location, requirements, and timer.
- Read the case. The player skims the skill tags ("STH REQ", "FRC +") against the roster on the right.
- Dispatch. Click the call → click a compatible operative. Travel time begins. Operative dot leaves HQ and routes along streets.
- On scene. Resolution timer ticks. Skill match determines outcome; expert-level matches earn a clean-resolution bonus.
- Return. Operative routes back to HQ. Earnings paid, heat adjusted, log line printed. Repeat for ~30 calls across the shift.
- Sunrise. Tally — closed, lost, take. Heat carries; cash unlocks operatives and equipment for the next shift.
03 The roster
Six starting operatives, each with three of five skills at varying levels. Skill ratings are 0 (none), 1 (capable), 2 (skilled), 3 (expert). To clear a case all required skills must be ≥1; ≥2 unlocks a "clean" bonus (+25% pay, –heat). Below 1 means a 35% scrape-by chance — and a burned approach on failure.
W
Maya "Wraith" Okonkwo
FIELD OPERATIVE · CALLSIGN WRAITH
STH · 3TCH · 2
FRC · 1NEG · 1
Ex-Treasury. Goes through doors like she pays rent.
P
Jonas Pell
FIELD OPERATIVE · CALLSIGN PELL
FRC · 3MED · 2
STH · 1NEG · 1
Three combat tours. Field-medic certified.
C
Vivien Crowe
CLOSER · CALLSIGN CROWE
NEG · 3STH · 2
FRC · 1MED · 1
Talked a man off the Mercer ledge in 11 minutes.
S
Otis "Switch" Lange
TECHNICIAN · CALLSIGN SWITCH
TCH · 3STH · 2FRC · 2
Half engineer, half locksmith. Always the half you need.
D
Pearl "Doc" Hannigan
MEDIC / NEGOTIATOR · CALLSIGN DOC
MED · 3NEG · 2
STH · 1TCH · 1
ER nurse for ten years. Knows what people in bad spots need.
R
Reza "Rook" Khouri
FIELD OPERATIVE · CALLSIGN ROOK
FRC · 2NEG · 2
STH · 1TCH · 1MED · 1
Boxer turned process server. Reads a room in five seconds.
Operative states
- Available — at HQ, ready to deploy.
- En Route — traveling to scene; ETA visible.
- On Scene — working the case; resolve timer ticks.
- Returning — heading back to HQ.
- Resting — forced cooldown after high-difficulty case (≥ chapter 2).
- Down — wounded; out for the rest of the shift (≥ chapter 3).
04 Call catalog
Calls are generated from templates. Each template defines required skills, optional bonus skills, base timer, on-scene resolve time, and payout. Locations rotate from a noir name pool.
| Code | Type | REQ | BONUS | Timer | Resolve | Pay |
| 10-22 | Tail Subject | STH | TCH | 75s | 11s | $120 |
| 10-17 | Bar Disturbance | FRC | NEG | 55s | 9s | $90 |
| 10-91 | Hostage Standoff | NEG | FRC | 70s | 14s | $200 |
| 10-39 | Wiretap | TCH | STH | 95s | 13s | $150 |
| 10-54 | Welfare Check | MED | NEG | 80s | 10s | $110 |
| 10-66 | Bag Job | TCH · STH | — | 110s | 16s | $240 |
| 10-32 | Shake / Tip | NEG | FRC | 65s | 10s | $130 |
| 10-83 | Witness Escort | FRC | MED | 85s | 12s | $170 |
| 10-71 | Cipher Pickup | TCH | STH | 90s | 11s | $140 |
| 10-12 | Tail + Capture | STH · FRC | — | 100s | 15s | $260 |
Urgency
Each call rolls a chance to spawn PRIORITY (urgent). Urgent calls pulse red on the map, fail with 14 heat instead of 8, and tighten timers by 25%. Urgency probability rises through the shift: 18% at 19:00 → 53% at 02:30.
05 Screen layout
Top bar Shift clock · open cases · closed · earnings · city-heat meter · channel ID
Calls panel Stack of incoming cards. Each shows code, type, location, REQ/BONUS chips, urgency, countdown bar. Top of stack is most recent.
▣ THREE.JS CITY VIEW ▣
isometric ortho · 7×7 block grid
HQ at center · lit windows · neon signs · rain
call beacons pulse on cell · operative dots route streets
Roster panel All operatives. Avatar · codename · role · top skills · live status. Compatible ops glow when a call is selected.
Radio log Time-stamped chatter: dispatch · unit · alert · resolve. Right side: equalizer, channel meter, weather.
Interaction model
- Click a call card → highlighted, compatible ops glow amber, others dim.
- Click a compatible op → dispatched. Click an incompatible op → ignored (with a brief flash).
- Click the highlighted call again → deselect.
- Hovering a call shows a callout near its map beacon with full case detail.
- WASD / arrows nudge the camera (optional polish).
06 Numbers & tuning
Travel time
// scene grid units → seconds
travelTime(from, to) = 2.5 + manhattanDist(from, to) * 0.9
// HQ at (0, 0). Furthest cell ≈ 6 units away → ~7.9s one way.
Outcome math
function evaluate(op, call) {
let miss = false, bonus = 0;
for (r of call.req) {
if (op.skill[r] < 1) miss = true;
if (op.skill[r] >= 2) bonus += 1;
}
for (b of call.bonus) {
if (op.skill[b] >= 1) bonus += 0.5;
}
if (miss) {
// 35% scrape-by per missing requirement
chance = Math.pow(0.35, missing.length);
ok = Math.random() < chance;
} else ok = true;
return { ok, bonus };
}
Economy
- Payout = base × (1 + bonus × 0.25). A clean two-skill match pays ~50% over base.
- Failure = +8 city heat, no payout. PRIORITY failure = +14 heat.
- Clean clearance = –3 heat per closed case.
- Shift target (Ch.1): 8+ closures, heat < 40 at sunrise.
Heat consequences
- 0–30 · Quiet. Normal operations.
- 30–60 · Watched. Reduced response window (–15% timers).
- 60–85 · Hot. Cops listen in on Ch.7 — chance of "10-99" calls that lock an op out of the next case.
- 85–100 · Burnt. Agency on notice. Shift can end early; carries to next night.
07 Progression
The shell is a campaign of seven chapters, each a night with a new wrinkle and a tighter target. Cash carries between shifts and spends on the roster.
Chapter 1
A Quiet Wednesday
Tutorial cases. Three op types. Heat doesn't bite. Target: 6 closures.
Roster: 6Target $850
Chapter 2
The Mercer File
Recurring suspect across three cases. Compound timers — fail one, the rest stiffen.
Resting stateTarget $1,400
Chapter 3
Election Week
Politically-charged calls. PR cost on failure. Negotiators in heavy demand.
Down stateTarget $2,100
Chapter 4
Pier 9
Pier district unlocked. Longer routes, bigger payouts. Two-op calls debut.
Co-dispatchTarget $3,000
Chapter 5
A Wire on the Wire
Internal mole. Two of your ops can't be on the same case. Trust meter introduced.
Trust systemTarget $3,800
Chapter 6
Blackout
Power grid fails twice during the shift. Map goes dark. Tech ops compensate.
Map fogTarget $4,600
Chapter 7 is the long night — no fixed target, just survive until 04:00. Score posts to a personal leaderboard.
Between shifts
- Hire — new operatives drop into the bullpen; skim resumes, pick one ($400–$2,000).
- Equip — radios (+ETA accuracy), gear (skill +1 for one shift), favors (cancel one heat strike).
- Train — bump a skill on an op (slow, expensive — Chapter 5+).
08 Art direction
The look: low-poly noir, lit by windows.
A small city block at 2am. Wet streets, sodium-lamp halos, neon signs that pulse on the long beat. Buildings are simple boxes whose only detail is which windows are lit and what color. The mood does the heavy lifting.
Type
- Instrument Serif — italic, for character. Titles, op names, scene text. Editorial noir feel.
- JetBrains Mono — for everything else. Codes, timers, log lines, panel chrome.
- No third typeface. No emoji.
Palette
3D guidelines
- Orthographic camera, ~30° down, ~45° around Y. Subtle Lissajous breath on position.
- Lambert materials only on buildings. No PBR. No shadows.
- Windows = unlit emissive planes inset 0.01u from each facade. ~65% on; 7% blink.
- Neon = additive plane + slightly larger additive glow plane behind.
- Rain = 1,400 points, vertical streak material, slight wind slant.
- Cars = single dark box + two headlight spheres + faint headlight cone plane. Spawn every 3-7s.
- Fog: linear, [40 → 110]. Hides grid edges, sells depth.
HUD guidelines
- Panels: 1px solid border, dashed dotted corner ticks at 8px from each corner.
- All panel headers: 10px mono, 0.16em letter-spacing, uppercase, ink-dim. Tiny cyan dot indicator.
- Animations: gentle. 150ms ease on hover; pulse animations 1.4–1.6s.
- Scanline overlay over scene (overlay blend, 35% opacity). Soft vignette.
09 Audio
Mostly diegetic. The world is dressed by sound, not score.
- Bed — rain on glass, distant traffic, the dispatcher room's HVAC. Loops.
- Radio — squelch and brief chatter when a call lands; static under important events. Lo-fi band-passed voice through a single channel.
- Music — sparse jazz needle drop at 3 moments per shift (start, mid, end). Brushed snare, upright bass, a clarinet that's seen things. Royalty-free or commissioned.
- SFX — typewriter tick when new lines hit the log; a soft chime on clean clearance; a brief klaxon (filtered) on PRIORITY spawn.
10 Tech notes for the build
Stack
- three.js (r160+) for the city. WebGL renderer, antialiasing, pixel-ratio capped at 2.
- React 18 for the HUD overlay (or Preact if bundle size matters).
- Vanilla state — one reducer for game state, useRef for animation-frame loop.
- localStorage for save: roster, cash, chapter progress, leaderboard.
File map (mirror the prototype)
/index.html // shell — pinned three.js + react + babel
/scene.js // three.js world. Exposes NightshiftScene API:
// init(host), setCallMarker(id, cell, urgent),
// spawnOperative(id, cell, color),
// moveOperative(id, cell, durationSec),
// flashResolve(cell, success), worldToScreen(...)
/game-data.js // ROSTER, CALL_TEMPLATES, evaluateMatch, travelTime
/ui.jsx // React HUD + game loop (state machine)
/styles.css // noir tokens, panel chrome, type scale
/assets/audio/*.mp3 // rain, radio chatter, jazz beds, SFX
Scene → UI contract
The scene owns visuals only. The UI owns time, state, and decisions. Scene exposes pure imperative calls and is otherwise headless. This keeps both halves swappable.
Performance budget
- 60 FPS on a 2019 MacBook Air at 1440×900 — verified in prototype.
- Scene mesh count ≤ 1,800 (currently ≈ 1,200). Rain count tunable.
- Single draw call per call-marker group via instancing if marker count exceeds 12 simultaneously.
Save schema
{
"chapter": 3,
"cash": 4280,
"roster": ["wraith", "pell", "crowe", "lange", "hannigan", "khouri", "new-hire-2"],
"opXp": { "wraith": { "Stealth": 12 } },
"gear": ["radio-mk2", "favor-pd"],
"leaderboard": [{ "night": 3, "score": 14200 }]
}
11 Build plan for Claude Code
Stage the build in three milestones so each is verifiable in isolation.
- Milestone 1 · Prototype loop (this doc's prototype)
Single shift, single chapter, six fixed ops, ten call templates, no save. Confirm the feel.
- Milestone 2 · Campaign shell
Chapter selection, between-shift store, save/load, full operative XP, six chapters of templates.
- Milestone 3 · Narrative + audio
Recurring NPCs and the Mercer thread, full audio bed, jazz cues, the long-night chapter and leaderboard.
Out of scope (v1)
- 3D operative models — the dot abstraction is the visual language.
- Multiplayer / co-op dispatching.
- Procedural city beyond fixed grid + seeded variety.
- Dialogue choice trees inside scenes (calls resolve on skill check only).
12 Known risks
- Call density — too sparse and the player idles; too dense and the queue blocks. The 9–16s spawn window with ramp may need per-chapter tuning curves.
- Op assignment friction — two-click flow (call → op) is correct for the puzzle, but a drag affordance might be wanted. Build behind a setting.
- Heat death-spiral — bad first few minutes can lock players out of recovery. Floor heat reduction at 1/minute baseline regardless of clearances.
- Reading load — call cards are dense. Watch tester eyes; consider an "expert mode" toggle that hides bonus tags.