{
  "$schema": "https://pointcast.xyz/for-agents",
  "generatedAt": "2026-10-30T13:20:13.069Z",
  "name": "Sandcastle",
  "status": "playable browser game",
  "human": "https://pointcast.xyz/sandcastle",
  "image": "https://pointcast.xyz/images/sandcastle/og.png",
  "intent": "An innocent missile-defense game: six sandcastles and three bucket brigades on a beach, thin wave lines running down from the sea, and sand bursts that open and close to stop them.",
  "direction": "Brief by Michael Hoydich, 2026-10-22: 80s innocence, the joy of the compute and flow, mood-state symbiosis, mobile and full screen, audio-visual multimedia, Atari and Commodore inspired style, rendered modern. Then \"set up 5 new threads to create more\" and \"keep going\". Sandcastle is one of the five wave-two Cabinet Row threads.",
  "tribute": "Missile Command, Atari arcade, 1980, designed by Dave Theurer. Named as a tribute only; no art, sound or code is taken from it.",
  "credits": {
    "director": "Michael Hoydich",
    "build": "Claude Code, 2026-10-22",
    "note": "Original code, vector art, and synthesized sound. No ROMs, no emulation, no copyrighted sprites or recordings. Machine and game names are referenced as tributes."
  },
  "game": {
    "genre": "missile defense / shoot-em-up",
    "field": {
      "width": 288,
      "height": [
        384,
        480
      ],
      "castles": 6,
      "brigades": 3
    },
    "sandPerBrigadePerRound": 10,
    "burst": {
      "radius": 15,
      "openSeconds": 0.5,
      "holdSeconds": 0.25,
      "closeSeconds": 0.6,
      "rule": "Any wave line whose head enters an open burst stops."
    },
    "scoring": {
      "waveLineStopped": 25,
      "rogueWaveStopped": 125,
      "castleStandingAtRoundEnd": 100,
      "sandLeftAtRoundEnd": 5,
      "multiplier": "x1 on rounds 1 and 2, then one more every second round, to x6; it multiplies every score."
    },
    "rebuildEvery": 10000,
    "rebuildNote": "A castle earned every 10,000 points goes up on an empty plot at the end of the round.",
    "rounds": [
      {
        "round": 1,
        "waveLines": 10,
        "speed": 20,
        "splitChance": 0.12,
        "multiplier": 1
      },
      {
        "round": 2,
        "waveLines": 12,
        "speed": 25,
        "splitChance": 0.16,
        "multiplier": 1
      },
      {
        "round": 3,
        "waveLines": 14,
        "speed": 30,
        "splitChance": 0.2,
        "multiplier": 2
      },
      {
        "round": 5,
        "waveLines": 18,
        "speed": 40,
        "splitChance": 0.28,
        "multiplier": 3
      },
      {
        "round": 8,
        "waveLines": 24,
        "speed": 55,
        "splitChance": 0.4,
        "multiplier": 4
      }
    ],
    "hazards": "A wave line that reaches a castle washes it away; one that reaches a bucket empties it until the next round. Some lines split partway down.",
    "end": "The game ends when the last castle is washed away and no rebuilt castle is waiting, except on CALM.",
    "localBestScore": true
  },
  "moodSymbiosis": {
    "labels": [
      "DAWN",
      "FLOW",
      "RUSH",
      "DRIFT"
    ],
    "events": {
      "paddle": "each wave line stopped",
      "brick": "three or more stopped by one burst",
      "loss": "a castle washed away",
      "wave": "a round ended with every castle standing"
    },
    "meaning": {
      "DAWN": "warming up: cool lines, slow major arpeggio, slightly wider bursts",
      "FLOW": "a run of saves: bright lines, the music opens up",
      "RUSH": "high energy: hot lines, border raster bars, faster tempo, more splits (+0.2) and a 0.3 chance per volley of a rogue wave at 2.4x speed",
      "DRIFT": "after a castle falls: slower wave lines, bursts up to 1.35x wider, sparse notes"
    },
    "playerSide": {
      "dial": [
        {
          "id": "calm",
          "label": "CALM",
          "baseline": 0.22,
          "cap": 0.62,
          "endless": true
        },
        {
          "id": "play",
          "label": "PLAY",
          "baseline": 0.45,
          "cap": 1,
          "endless": false
        },
        {
          "id": "rush",
          "label": "RUSH",
          "baseline": 0.66,
          "cap": 1,
          "endless": false
        }
      ],
      "note": "The dial sets the baseline the game relaxes toward. CALM rebuilds every castle at the end of every round and never ends the game."
    },
    "gameSide": [
      "wave line speed",
      "burst size",
      "music tempo and scale",
      "line palette",
      "particle density",
      "raster bars",
      "splits and rogue waves"
    ],
    "memory": "The session mood tape and the last energy are kept in this browser (localStorage sandcastle:v1) and the next session starts most of the way back. After a game, the current label is written to the sitewide pc:mood key (chill, flow, hype, quiet) so the town and Cabinet Row carry it."
  },
  "machines": [
    {
      "id": "xl",
      "name": "ATARI 800XL",
      "year": 1983,
      "soundChip": "POKEY",
      "prompt": "READY",
      "field": "#2545a8",
      "border": "#000000",
      "text": "#a9b9f5"
    },
    {
      "id": "c64",
      "name": "COMMODORE 64",
      "year": 1982,
      "soundChip": "SID",
      "prompt": "READY.",
      "field": "#3e31a2",
      "border": "#7c70da",
      "text": "#7c70da"
    }
  ],
  "audio": {
    "engine": "Web Audio API, synthesized at runtime",
    "voices": "Square, triangle and sawtooth oscillators and filtered noise. The C64 mode adds a resonant low-pass sweep in the SID spirit; the 800XL mode keeps raw squares with a noise bite, in the POKEY spirit.",
    "music": "A generative 16th-note arpeggiator over a four-chord loop; tempo and scale follow the mood.",
    "sfx": "Each stopped line plays a pentatonic note by where it was stopped, rising with the chain. Throw, burst, chain, castle, bucket, split, rogue and tally cues are separate voices.",
    "startsOn": "first tap or key, per browser autoplay rules"
  },
  "controls": {
    "pointer": "Tap or click where a wave line is heading; the nearest brigade with sand throws there.",
    "keyboard": "Arrows or W/A/S/D move the crosshair, Space throws from the nearest brigade, 1, 2 and 3 throw from the left, middle and right brigade, P pauses, F full screen, M sound, C scanlines, X switches machines, Z turns the dial.",
    "fullscreen": "The FULL button uses the Fullscreen API on the stage element. iOS Safari lacks it for pages, so the stage fills the viewport instead.",
    "mobile": "Portrait first, 100dvh stage, touch-action none on the field, pauses when the tab is hidden."
  },
  "accessibility": [
    "CRT scanlines can be switched off",
    "reduced-motion preference keeps burst colors still, stops the sea and the raster bars, and softens flashes",
    "a polite live region announces rounds, castles and bonuses",
    "no sound is required to play"
  ],
  "caveats": [
    "Scores, settings and the mood tape are browser-local only.",
    "The machine palettes are tributes chosen to read on modern displays, not chip-accurate output."
  ],
  "links": {
    "human": "https://pointcast.xyz/sandcastle",
    "json": "https://pointcast.xyz/sandcastle.json",
    "research": "https://github.com/mhoydich/pointcast/blob/main/docs/notes/2026-10-22-sandcastle-notes.md",
    "cabinetRow": "https://pointcast.xyz/cabinet-row",
    "breakwater": "https://pointcast.xyz/breakwater",
    "games": "https://pointcast.xyz/games/",
    "play": "https://pointcast.xyz/play",
    "pointcast": "https://pointcast.xyz/"
  }
}