# JustDig! design notes

## The question

How far can one shovel and one button go if every enemy plays fair? JustDig keeps the controls to a D-pad and a Pump button and spends its design effort on making each critter readable.

## The board

- 13 by 15 cells with a bedrock border, so 11 by 13 cells you can dig.
- The first wave always uses the same layout so the opening is learnable. Later waves pick one of six tunnel families (crossed loops, ladder, pockets, switchbacks, rings, crossroads) and may mirror it.
- Biomes change every three waves: Garden Crumbs, Soda Caves, Mooncake Depths, then around again.
- A run is seeded. Every wave's layout, roster, rocks and gems come from the run seed, so "Retry same seed" replays the same boards.

## Fair attacks

Every attack has a telegraph, an active window, a recovery and a cooldown. Timings are in 60 Hz ticks and stretch by 1.5x on the Relaxed pace.

| Critter | Telegraph | What the player sees | Answer |
| --- | --- | --- | --- |
| Nib bite | 24 ticks | A short wind-up next to you | Pump first or step away |
| Puffin breath | 54 ticks | Glowing breath and a marked lane up to 4 cells | Turn a corner |
| Mottle crossing | 60 ticks | Cracked dirt and a pink landing ring | Leave the ring |

Targets are committed when the telegraph starts. Nothing tracks you after that. A new tunnel or a rock can still block a Puffin's breath.

At most two critters can be winding up or attacking at the same time, and only one special attack (Puffin or Mottle) runs at once.

## Pressure

Each Nib costs 1 pressure and Puffins and Mottles cost 1.5, plus 0.5 when both are out. The board holds at most 5 (2 on wave 1). Extra critters wait in a queue and arrive with a 1.5 second spawn warning, never closer than five cells to the player.

If thirty seconds pass with no digging, gems or pops, the ground opens a straight tunnel toward the nearest critter.

## Pump and rocks

- The pump reaches two open cells in the direction you face. Three puffs (27 ticks each) pop a critter. Released critters deflate one stage at a time.
- A rock with open tunnel below shakes for 72 ticks, then falls one cell every 9 ticks until it lands. It crushes every critter it passes through.

## Scoring

Pump pop 100, rock crush 200 (+100 for each extra critter under the same rock), gem 50, wave clear 250. Records are kept separately for Standard and Relaxed.

## Testing by play

The rules are a pure simulation (`src/game/`) with no rendering inside. `src/qa/autopilot.js` is a pump bot that looks one move ahead through the real `stepRun` and either pumps a reachable critter or walks toward one. The experience, encounter, effects and presentation harnesses in `src/qa/` set up exact moments for screenshots and checks.

A pass on 2026-10-11 ran the bot over 40 seeds and 10 waves each: 400 of 400 waves cleared, no rescue tunnels needed, median wave length about 13 seconds, 90th percentile 22 seconds. Waves 1 to 12 for those seeds all passed `validateWave` (spawn open, critters reachable, pressure within the cap).

## Art

Character concepts and the three painted cave backdrops started as AI-generated images. The 32-pixel sprites were authored and animated per pixel: the digger has 7 actions, Nib and Puffin 9, Mottle 10, each in four directions. The character lab pages in `character-labs/` let you step through every clip and frame.

## Sound

`src/audio/gameAudio.js` plays samples through a small mixer: sfx, ui and stinger buses into a master with a gentle limiter, and ambience plus a quiet tonal bed through a duck stage. Stingers (hit, wave clear, game over, new record) and rock impacts pull the beds down for a moment so they read clearly.

`src/audio/soundKit.js` is the whole sound design in one table: which samples each cue uses, its bus, level, pitch spread, cooldown, voice limit, steal priority, ducking and the synth stand-in from `proceduralAudio.js` if the sample is missing. Cues never repeat the same variant twice running, pan with the critter's column, and get a small random level and pitch wobble.

| Moment | Cue |
| --- | --- |
| Dig a cell / walk a tunnel | dig crunch (pitched per biome) / soft step |
| Hose on, each puff, stage 2 | rubber snap, balloon puff pitched up per stage, critter squeak |
| Pop / rock crush / deflate / miss | balloon pop (+ soda fizz) / toy squish / air release / rubber-band flop |
| Nib | teeth chatter on wind-up, leather snap and lip smack on the bite |
| Puffin | rising swell on gather, party-blower blast with a little flame on release |
| Mottle | grinding crack on the telegraph, dirt dump and pop-up when it surfaces |
| Rock | grind on the warning, cartoon wipe as it drops, chest-on-dirt thud |
| Gems | ding, pitching up two semitones per gem inside 2.5 s |
| Hurt, respawn, wave clear, game over, new best | pan bonk + squeak, musical sparkle, short sting, trombone slide, longer sting |

Sources are from the Adobe Audition sound effects library (Cartoon, Foley, Impacts, Household, MultiMedia, Sports, Animals, ScienceFiction, Fire and Explosions, LiquidWater, Underwater, Drones, Ambience2), whose terms allow use inside a product but not redistribution of the files on their own. Each one was trimmed, faded, level-matched to an active-RMS target with a -1 dBFS ceiling and saved as 96 kbps mono MP3. The three beds are 24 s stereo loops mixed from two layers with an equal-power crossfade at the seam. The full set is about 1.3 MB.

## Start-up loader

`index.html` carries a generated list of every file the game needs: the import graph from `src/main.js` (with the three.js vendor files), the stylesheet, the JSON contracts, every sprite and backdrop in the art catalog and every sound. Rebuild it after changing any of those files:

```
node gamelab/tools/build-justdig-boot.mjs          # rewrite the list
node gamelab/tools/build-justdig-boot.mjs --check  # fail if it is stale
```

The build id is a hash of all those files. An import map and `src/version.js` add it as `?v=` to every module, image, JSON file and sound, so any change busts every cache at once. The loader fetches four files at a time, retries 5xx and network errors up to six times, names any file that fails, treats sounds as optional, reloads once with fresh addresses if a module trips while starting, and offers "Reset app cache" (a per-tab fresh suffix). It then hands off to `main.js`, which reports art and sound decoding on the same screen.
