Star Swarm — The Build Log
The milestone status reports kept during the build — formation, entrances, the review team, dives, the beam, and the rescue, as they landed.
_Updated 2026-08-29_
Milestone 1 — Formation: complete
- Player moves with ←/→ (or A/D), clamped to the screen, plus a short ↑/↓ (W/S) band — 80px forward, 12px back — for dodging; Space fires (max 2 bullets alive, edge-triggered).
- 40 enemies in 5 ranks (4 boss / 8 lancer / 8 lancer / 10 drone / 10 drone) pre-placed in a breathing, drifting formation.
- Bosses have 2 HP and turn pink at 1 HP. Scores: boss 150, lancer 80, drone 50.
- 3 lives (
loseLifeexists; nothing can kill the player yet). - Stage-clear overlay when the formation is empty; Enter advances to the next stage (same formation, score/lives kept).
- Entrance dives dip to y≈528, overlapping the ship's forward limit (top edge y=501) but clear of its rest height — pushing forward during a wave is a risk.
- Enemy→ship contact destroys the enemy (scored), costs a life; the lane stays empty for 1.6 s (explosion plays out), then the replacement ship arrives with a 'ready' cue and blinks through 1.5 s of grace.
- Shot/hit tracking per stage and per game. Stage clear awards an accuracy bonus by hit ratio (100% → 5000, ≥90% → 3000, ≥75% → 2000, ≥60% → 1000, ≥40% → 500) and shows shots / hits / ratio / bonus; game over shows the overall hit ratio.
- Scrolling starfield with horizontal parallax: stars counter-scroll against the ship's sideways motion, near stars more than far.
- 27 tests: formation math, collisions, boss HP, reducer behaviour, purity check, and a fixed-clock integration sim that clears the stage.
Milestone 2 — Entrances: complete (branch feat/m2-entrances)
src/logic/paths.js: Catmull-Rom splines, arc-length reparameterised (constant speed),mirrorPath, registrysweepLeft/sweepRight(mirror pair) /plunge.src/logic/waves.js: wave format validation (all errors collected), launch schedule, arrival blend retargeting the last 15% to the slot's projected-arrival position.- Enemy state machine
pending → entering → settling → inFormation; pending enemies invisible and untargetable. - Stage 1 = 4 waves over all 40 slots, entrance ≈12 s; stage 2+ reuse the table at speedScale 1.1.
docs/specs/wave-format.mddocuments the format.
Audio (branch feat/audio)
src/engine/audio.js: all sounds synthesized with Web Audio (oscillators + LCG noise) — nothing sampled or copied.- Events → sounds:
firedlaser zap,enemyLauncheddive whistle,enemyHitthunk,enemyKilledpop+burst,stageClearsting,lifeLostfall. - Formation hum: a tremolo-pulsed drone runs while enemies sit in formation; pitch, pulse rate and brightness rise as the formation thins.
- Unlocks on first key press (browser gesture rule); M toggles mute.
Milestone 3 — Review team: complete
feel/bugs/threatreviews → tuning changelist anddocs/specs/attack.md. Prerequisite fixes landed with M4 (stage-clear vs game-over precedence,pathPointNaN guard, arrival blendblendStart0.7).
Milestone 4 — The attack: complete (merged 2026-08-29, 249 tests)
src/logic/rng.jsseeded xorshift;dives.jscadence/unlocks/diver selection/templates parameterised by player x and stage (diveBottomYramps 490 → 530), return paths with the 100/170 px blend guardrails, aimed fire (drone straight, lancer closed-form lead, kamikaze once), escorts;capture.jssweepingbeamCone, capture accumulation, boss/player/captive/dual transition tables +transition().- Entities: enemy dive/return/escort/beam states, enemy bullets, captive, dual fighter (2 hitboxes, 2 origins, 2 bullets per ship, drop-to-single, persists across stages).
- Visuals/audio: animated beam cone drawn from the logic triangle, enemy bullets, captive above the boss, dual render, boss beaming tint, SHIP CAPTURED / FIGHTER RESCUED banners, HUD dual indicator, beam loop + 7 new synthesized cues, starfield dims while beaming.
- Tests: table-driven capture transitions (both boss-death branches, player-death-during-rescue, dual beam), seeded cadence bounds, fixed-clock dive paths, aim, escorts, fire cap, one-boss-off-formation, breakaway-vs-cap, stage-clear precedence, 60 s seeded attack sims.
Milestone 5 — Challenge pack: complete (merged 2026-08-30, 317 tests)
- Wave format:
challenge: truestages withcountgroups, off-screen-exit rule, global timing-overlap rule (MIN_PATH_GAP_PX40) — all backward compatible; documented indocs/specs/wave-format.md. - Five showpiece paths:
figureEight(full-screen, self-crossing),sidewinder+ mirror,loopDeLoop(true 540° loop),corkscrew. - Three challenge stages (stages 3, 6, 9, cycling): 40 enemies each, enter → showpiece → exit, no dives/fire; 36 s / 27 s / 36 s; zero enemy-enemy overlaps (min separation 32 / 29 / 27 px). Perfect clear (40 bullet kills) → +10,000; hit ratio still shown, no accuracy bonus.
npm run validate-wavesCLI (exit 1 on failure; accepts a module path).- UI/audio: CHALLENGE STAGE banner, HUD kills/40, CHALLENGE CLEAR screen with PERFECT / NO BONUS,
exitandperfectcues. - qa's scripted pilot reaches 23 / 31 / 24 of 40 — perfecting a challenge stage takes real play.
Normal-stage variety (2026-08-30)
- Four more entrance paths:
hookLeft/hookRight(low side entry, hook up),cascadeLeft/cascadeRight. - Stage 2 (bosses lead, cascades + hooks) and Stage 3 (drones rush low first, bosses last down the cascades) tables; normal stages cycle 1 → 2 → 3 between challenge stages. All validate; 318 tests.
iPhone / iPad (2026-08-30) — the Brick Break checklist applied
- Page lockdown CSS (touch-action none, overscroll none, fixed body, no callout/select),
viewport-fit=cover+ safe-area padding,100dvh, portrait 3:4 canvas fitted by width AND height. - DPR-scaled backing store (cap 3) in
src/engine/viewport.js, re-fit on resize / orientation / visualViewport. - Touch input (
src/engine/input.js, Pointer Events): drag anywhere = relative ship movement via the purenudgePlayer; quick tap = one shot (or start on menus); hold the bottom-right fire pad or a second finger = autofire; top-right tap = mute. Non-passive touchmove preventDefault for older Safari. Audio unlocks on first touch as well as keydown. - Pure-CSS "turn your phone upright" prompt for landscape phones. On-canvas touch hints appear once a touch is seen.
vercel.json+.vercelignorefor phone-test deploys; on LAN useipconfig getifaddr en0+ the port.- No
ctx.roundRectin the renderers (nothing to feature-detect).
Extra ships (2026-08-30)
- A bonus ship every 10,000 points (
EXTRA_LIFE_EVERY), awarded on the same step the score crosses the line (kills, accuracy and perfect bonuses all count); carried across stages.extraLifeevent → EXTRA SHIP banner + sting. A hit that both earns and costs a ship on the same step nets zero instead of game over.
Capture frequency retune (2026-08-30)
- Boss dives unlock below 75 % formation remaining (was 25 %), beam-intent roll 0.5 (was 0.2), line-of-sight window ±100 px (was 60). Constants:
DIVE_UNLOCK,BEAM.intentChance,BEAM.lineClearXRange.
Next
- Playtest tuning: challenge stage speeds (are perfects achievable?).
Rescue fix (2026-08-30)
Bug: after a capture the boss ascended home in ~1 s, beating the 1.6 s respawn, so the first shot the player could fire hit an in-formation captor → captiveDestroyed (silent — same kill cue as any other enemy). The re-dive rescue path worked in the reducer but the captor was never preferred for a launch, so in play "shoot the boss" never yielded the dual fighter. selectDivers now gives a captive-holding boss the next launch (attack.md §2 exception); main.js shows a CAPTIVE LOST banner on captiveDestroyed. Tests: captor-priority unit cases + an end-to-end rescue-loop sim in tests/integration/attack-sim.test.js.
Straight-down fire on early stages (2026-08-30)
Enemy shots on stages 1–3 now drop straight down (aimDown); aimed drone fire and lancer lead start at stage 4 (ENEMY_BULLET.aimedFromStage). Tommy's playability call — the early game was too punishing with every diver leading the ship.