Sandbox Mode

Sandbox mode is a run flag that suppresses the normal arcade-run subsystems so the mission becomes a tunable isolation chamber. It is the runtime backbone of the dev playground tabs (boss test, weapon test, artifact gauntlet, mods, feel) and is never used by player-facing arcade runs.

Activation

RunConfig.sandbox?: boolean on data/run-config.ts (defaults false). When runDef.sandbox === true the bridge takes the sandbox branch in engine/bridge.ts on every applicable subsystem. Telemetry records the flag at recordStart so any sample stream stays distinguishable from arcade runs.

What is disabled

When runDef.sandbox is set, bridge.ts skips:

  • Mission timer. game.missionElapsed += dt is gated behind !runDef.sandbox — elapsed time is frozen, so director / spawn ramps and any time-based level-end conditions never advance.
  • Overtime end condition. The overtime → game-over check is skipped, so the run cannot end on the wall clock.
  • Director. Director.update(game, ship, world, dt) (mood / pressure / personality) is suppressed.
  • GameMaster spawner. The trickle / waves / retreat / flank tick only runs if game._sandboxSpawnerEnabled is explicitly toggled on via the right-panel control (setSpawnerEnabled). Default is off.
  • Leveling. LevelingSystem.update(game) (XP threshold → level-up card popups) is skipped.
  • Event reward pipelines. Map events still trigger juice for layout walkthroughs, but continue short-circuits the artifact / hub-illumination reward flow so playground-controlled run state isn’t corrupted.
  • Disabled chest / artifact KPM schedulers. The legacy timed-drop blocks (currently if (false)-gated) also include the sandbox guard for the day they’re revived.
  • Launch SFX + music. MusicPlayer.start() and SampleSfx.playLaunch() are skipped on mission.start() because sandbox missions rebuild frequently.

What is enabled

  • Frame scheduler swaps to setTimeout(16). Sandbox uses setTimeout instead of requestAnimationFrame because Claude Preview throttles rAF to ~1fps even when document.hidden is false. setTimeout in a visible tab runs at the requested interval (~60fps).
  • Dev console API. PERF_FLAGS.devScenario || runDef.sandbox exposes window.__dev (and window.__mission) for Playwright / Claude automation: spawn elite, clear enemies, set HP / shield / heat, god mode, freeze, screenshot, grant artifact, set speed, autopilot, weapon gauntlet, etc.
  • Sandbox mutators on the MissionHandle. These mutate live engine state without a mission rebuild — the price of avoiding rebuilds is that some tunings stack against a baseline snapshot.

Sandbox API surface

Exposed on the MissionHandle (see “SANDBOX API” block in engine/bridge.ts):

MethodPurpose
setSpawnerEnabled(enabled)Toggle GameMaster.tick (default off in sandbox).
sandboxSpawnBoss(bossId, tier)Spawn a boss into a fresh closing arena. Resets bossRoom / bossArena first so successive gauntlet runs always get a new shrinking-wall instance — otherwise stale spawnTime prevents the closing wall from reappearing.
sandboxGrantArtifact(artifactId)Each call grants +1 tier (level up if already owned).
sandboxEquipMods(mods)Re-equip mods against a one-time baseline snapshot of ship stats. Does not restart the mission. Caveat: a patchShipStats tweak made after the first equip is wiped on the next equip because the baseline is frozen.
sandboxRespawn()Restore ship HP / shield and return to playing phase after death.
sandboxResetForTest()Full reset for the artifact / weapon gauntlet — clock to t=0, frame-loop accumulator + lastFrameTime, ship HP / shield / heat, kills, damage stats, artifacts, enemies, speed. Does not rebuild the world or terrain.
sandboxSetDebugOverlay(on)Toggle collision-debug overlay.
sandboxSetLighting(...)Writes to game.vision for back-compat with the playground UI; no renderer consumes it.

The dev console’s __dev.playground.resetForTest() and __dev.weaponGauntlet() both route through _missionHandle.sandboxResetForTest(). The weapon gauntlet additionally requires the page to be on /ship-playground because a live mission must exist for the reset to operate on.

Relationship to other modes

Sandbox mode is orthogonal to the arcade run pipeline — it is never produced by assemble-run for a player-facing run. It is set up explicitly by the dev playground entry points. PERF_FLAGS.devScenario (URL param ?dev=<scenario>) is a separate dev path that shares the same __dev console surface but does not flip the per-system gates above.

Why these gates exist

  • Tuning a single enemy / boss / weapon needs steady-state damage / kills / time without the director feathering pressure based on imagined player skill.
  • Re-running the same gauntlet slice repeatedly needs deterministic state (Clock.reset() plus the reset of accumulators and run-stat counters).
  • The artifact reward pipeline assumes a normal run economy; firing it inside a playground-controlled run breaks subsequent artifact picks.
  • Mission rebuilds are slow and visually noisy; sandbox prefers hot-swap mutators that mutate the live ship / artifact / mod state in place.

See also

  • data/run-config.tsRunConfig.sandbox field (line 435).
  • engine/bridge.ts — sandbox gating at runDef.sandbox sites and the SANDBOX API block.
  • engine/bridge-sandbox-utils.tsmakeBlockerVerts and other sandbox geometry helpers.
  • Dev playground tabs in the metagame shell consume the __dev.playground namespace.