Phaser + TypeScript, AI-first.
A BoringStack template.
A reusable, strictly-typed Phaser 4 starter so AI agents can ship at high velocity without eroding structure — and so a programmer who has never shipped a game can become a one-person studio in a weekend.
This repo is part of boringstack-xyz. Sister projects: the BoringStack fullstack template, tsforge (the TypeScript build harness), and eslint-plugins.
Backend engineer, tech lead, web developer who always wanted to make games? Start with docs/learn/ — a 2.5-hour primer that bridges "I can write code" to "I can generate art, design a core loop, and ship on itch.io."
Start a new game with tsforge (
/scaffold→ Phaser, ortsforge scaffold --archetype phaser --dest ./my-game), or click "Use this template" on GitHub. Then readBUILD_THE_GAME.md.
- Enable GitHub Pages —
Settings → Pages → Build and deployment → Source: "GitHub Actions". Without this thedeploy-pagesworkflow 404s on the first run. The org demo is served at phaser.boringstack.xyz (Chrome treatsboringstack-xyz.github.ioas a lookalike ofboringstack.xyz). Forks keep the defaulthttps://<user>.github.io/<repo>/URL. - Allow Actions to open PRs (so release-please can cut release PRs) —
Settings → Actions → General → Workflow permissions → [x] Allow GitHub Actions to create and approve pull requests. - Flip "Template repository" —
Settings → General → Template repositoryso your own "Use this template" button works.
Everything else (CI, Scorecard, CodeQL, Dependabot) works out of the box. Desired GitHub settings live in .github/desired-repo-settings.json.
- Working vertical slice — one player, three pickups, walls, live HUD, save/load to localStorage. Proves the architecture end-to-end and is the pattern to mimic.
- Architectural boundaries enforced by two independent tools —
eslint-plugin-boundariesin the editor/PR,dependency-cruiseras belt-and-braces in CI. Try importingphaserfromsrc/domain/**and watch both reject it. - Seven code generators (
bun run new:module|scene|feature|port|content|adr) so AI agents scaffold boilerplate instead of re-deriving it. - Spec-driven feature workflow via GitHub Spec Kit (
/speckit:specify → :clarify → :plan → :tasks → :analyze → :implement) with a project constitution pointing at the architectural rules. - Canonical AI-facing docs (
AGENTS.md,docs/ai/*) so every fresh session starts with the same context. - CI that enforces it all —
bun run check, Playwright smoke, CodeQL, Semgrep, OSV +bun audit, Gitleaks, OpenSSF Scorecard, plus a grep-based invariants job. - Release & dep automation — release-please for semantic versioning from conventional commits; Dependabot for grouped weekly updates.
| If you are... | Read next |
|---|---|
| A programmer who never shipped a game | docs/learn/ |
| A human starting a game (tsforge or GitHub template) | BUILD_THE_GAME.md |
| An AI agent in a fresh session | AGENTS.md + docs/ai/catalog.md |
| Just evaluating the architecture | docs/ai/architecture.md |
| Looking for the rules | docs/ai/contribution-contract.md + .specify/memory/constitution.md |
bun install
bun run dev # http://localhost:5173Arrow keys or WASD to move. Walk onto a yellow circle to score. Press S to save, R to reset.
bun run check is the merge bar (typecheck, lint, format, knip, dep-cruise, tests). bun run validate is that plus the Playwright smoke.
tsforge is the BoringStack TypeScript build harness.
New game — same wizard as BoringStack:
tsforge scaffold --archetype phaser --dest ./my-game
# or, in a tsforge session: /scaffold → Phaser → folder name
cd my-game
tsforge # first prompt plans the game (scene / feature / content slices)No Docker, no .env. The clone keeps this template's WASD demo; new work extends it. The gate is bun run check. Requires a tsforge build that includes the Phaser adapter (tsforge main; the next npm release after 0.51.1).
Existing clone — point tsforge at this tree. The phaser rule pack auto-applies from the phaser dependency (scene shutdown, no factories in update, branded keys). This repo's eslint.config.js covers a syntactic subset of that pack so bun run check stays honest without depending on a published tsforge.
Do not add .tsforge/scaffold-manifest.json here. That file is how tsforge configures the fullstack BoringStack template. tsforge writes .tsforge/scaffold.json (a receipt) after a Phaser scaffold; that is expected.
+--------+
| app | composition root
+--------+
|
+-----------+--+--+-----------+
| | | |
+--------+ +--------+ +---------+
| domain | |content | | shared | pure + data + common types
+--------+ +--------+ +---------+
|
+--------+
|runtime | Phaser, browser APIs
+--------+
| Layer | Responsibility |
|---|---|
domain |
Pure state + behaviors. No engine. No wall-clock. No storage. |
features |
Orchestrate domain + ports. Emit events. Never import Phaser. |
runtime |
Phaser scenes, entities, input, audio, adapters for shared ports. |
content |
Zod-validated definitions, levels, balance. |
shared |
Leaf utilities — types, event bus, test fakes. |
app |
Composition root. Wires ports, content, and the web entrypoint. |
Full story: docs/ai/architecture.md.
Phaser 4 · TypeScript 6 (strict + verbatimModuleSyntax + noUncheckedIndexedAccess) · Vite 8 · Vitest 4 · ESLint 10 (flat) · dependency-cruiser · Playwright · Zod · husky + lint-staged · knip · Bun 1.3.14 · Node 24.
| Script | What it does |
|---|---|
bun run dev |
Start Vite dev server |
bun run build |
Typecheck, then production build |
bun run test |
Vitest unit + integration |
bun run test:smoke |
Playwright smoke against a built bundle |
bun run check |
The gate: typecheck + lint + format + knip + dep-cruise + test |
bun run validate |
check plus Playwright smoke |
bun run check:arch |
Just architectural checks (lint boundaries + dep-cruiser) |
bun run catalog |
Regenerate docs/ai/catalog.md from src/ |
| Script | Generates |
|---|---|
bun run new:module <Name> |
Domain module under src/domain/<name>/ |
bun run new:scene <Name> |
Phaser scene |
bun run new:feature <Name> |
Feature orchestrator + test |
bun run new:port <Name> |
Port interface stub + matching fake |
bun run new:content <Name> |
Zod schema + sample JSON + validated loader |
bun run new:adr "<Title>" |
Numbered ADR in docs/adr/ |
| Project commands | Spec Kit commands (/speckit:*) |
|---|---|
/check |
/speckit:specify |
/new-module |
/speckit:clarify |
/new-feature |
/speckit:plan |
/new-scene |
/speckit:tasks |
/review-slice |
/speckit:analyze |
/verify-boundaries |
/speckit:implement |
/speckit:checklist |
|
/speckit:constitution |
Every push/PR runs:
ci—bun run check(typecheck, lint, format, knip, catalog, dep-cruise, tests) plus Playwright smokecodeql— static analysis withsecurity-and-qualityqueriessecurity-deps— osv-scanner onbun.lock+bun audit --audit-level=highsecurity-sast— Semgrep (OWASP top ten + JS/TS)security-secrets— Gitleaks (pinned binary, SHA256-verified)arch-invariants— grep-based banned-pattern guard (phaser/Math.random/Date.now/localStorage insrc/domain)
On main, OpenSSF Scorecard publishes SARIF weekly. release-please opens/updates a release PR from conventional commits. Dependabot opens grouped weekly PRs for bun and GitHub Actions.
MIT — see LICENSE.