A deck of client-side cyberpunk creative tools. Most apps here are a "program" that runs on the deck — it shares the visual language and code patterns, but ships, versions, and deploys on its own. One is not: the hub is the deck's chrome, a third category that consumes no user material and produces no artifact (ADR 0025).
No backend server — everything runs in the browser.
CYBERDECK (apps/deck) — the hub: it names the four programs, describes them,
and sends you into them. It is the deck's chrome, not a program — it consumes no user material,
produces no artifact, and is about nothing but the deck itself, which is what keeps it outside the
tool/piece fence rather than spending its one exception
(ADR 0025).
Live →
| Program | Path | What it does |
|---|---|---|
| ASCII//Convert | apps/ascii | Image or webcam → interactive ASCII art. Live → |
| GLITCH//Studio | apps/glitch | A glitch-effect pipeline over image or webcam — live preview, presets, PNG / video export. Live → |
| GOLEM//Console | apps/golem | A 32-bit fantasy computer: write assembly, assemble it, run it from a command line as registers, memory and the Terminal update live. Live → |
| SPRAWL//Atlas | apps/sprawl | The deck's first piece, not tool: the world's connected capacity as light. Opens blown white; you repair it by sliding the scale coarser until structure emerges. Live → |
Requirements: Node.js 22+ (see .nvmrc)
npm install # installs every app (npm workspaces)
npm run dev # start ASCII//Convert
npm run dev:deck # start the hub
npm run build # build every app
npm run test:run # run every app's tests once
npm run test:e2e # smoke E2E against every workspace's built output (Playwright)
npm run check # Biome lint + format, repo-wide
npm run bundle:budget # gzipped size of every app's build, against its ceilingtest:e2e builds each workspace and serves its dist itself, one Playwright project per workspace
— so a failure names the program before it names the guard. The browser is a one-off
npx playwright install chromium.
typecheck ends with the root tsconfig.json, which is the e2e suite's project and the only thing
at the root TypeScript compiles: the specs sit in no workspace, and they import app source.
bundle:budget reads the dist/ a npm run build left behind, so run the build first — on the
Node in .nvmrc, since zlib's deflate differs between majors and the ceilings were measured there
(the script warns if it does not match). CI runs the same check on every PR and prints the delta
against the base branch. The ceilings, and why there are two of them per app, are in
bundle-budget.config.mjs.
Scope any app script with --workspace @cyberdeck/ascii.
apps/ascii ASCII//Convert
apps/deck the hub — chrome, not a program (ADR 0025)
apps/glitch GLITCH//Studio
apps/golem GOLEM//Console
apps/sprawl SPRAWL//Atlas
packages/deck-kit the shared shell every program builds on
scripts/ repo-wide build-time tooling (the social cards and icon sets)
docs/adr architectural decisions, deck-wide
CONTEXT-MAP.md how the programs relate
apps/ holds the deck's workspaces, not only its programs: since the hub landed there, one of
them is deliberately not a program.
Tooling is deliberately light: npm workspaces, no Nx or Turborepo. Repo-wide tooling (Biome, lefthook, commitlint, Changesets) sits at the root; each app owns its own build and test dependencies. Playwright joins them there rather than living in an app, because what it checks — the Tailwind purge, the pre-paint Theme — only exists once an app has been built.
For a long time there was no shared packages/ — duplication was kept as a signal of what
actually repeats (ADR 0011). GLITCH//Studio made the
seams obvious, so the proven-shared surface was extracted into
@cyberdeck/deck-kit — the visual language, ui/ primitives, and
generic browser plumbing. It's consumed as source (no build step) and is deliberately not a
domain core: each app's pipeline stays in the app (ADR 0014).
Each workspace is its own Vercel project pointing at this repo. There is no root vercel.json: all
five set Root Directory to their apps/<name> and are driven by that workspace's own
vercel.json, which cds to the repo root so the @cyberdeck/deck-kit workspace dependency
resolves.
Each project's ignoreCommand skips its build when the diff touches nothing it ships. It watches
the workspace plus packages/deck-kit (consumed as source by all,
ADR 0014), diffs over
$VERCEL_GIT_PREVIOUS_SHA..HEAD (that project's last successful deploy on the branch), excludes
**/*.md, and fails toward deploying — production, a branch's first deploy, and any git error all
build.
That last case is what the trailing ; [ $? -eq 0 ] buys, and it is the reason not to tidy it
away. Vercel reads exit 0 as "skip" and exit 1 as "build"; any other status is a failed
deployment, not a build. git diff exits 128 when it cannot resolve $VERCEL_GIT_PREVIOUS_SHA
— which is what a force-push does, orphaning the commit that project last deployed from a shallow
clone — so without the clamp the ignore step errors every deployment on that branch instead of
building it, and it keeps erroring, because the pinned SHA only advances on a successful deploy.
The clamp maps every non-zero status onto 1, which builds. JSON takes no comments, so this
paragraph is where that reasoning lives.
CI's paths-ignore is separate; Vercel does not read it.
Commits follow conventional commits (enforced by
commitlint). Releases run on Changesets, per app — a PR that changes
app behavior adds one with npm run changeset
(ADR 0012).
GOLEM//Console's instruction set is inherited from Poxim, the didactic 32-bit architecture
used in the Computer Architecture course at UFS (Universidade Federal de Sergipe), 2017.
The encoding, register file, and 42 mnemonics are Poxim's; the reference programs under
apps/golem/src/golem/__fixtures__/ are the course's example project, kept verbatim as test
oracles. Credit to the course and its instructor —
ADR 0019 explains why the ISA was inherited
rather than designed.