SP slotplate
Start here

10-minute tour

A guided walk through the generated client, in reading order.

You've run npm create slotplate my-slot and pnpm dev. Open the project. Here's what's inside, in the order it makes sense to read.

1. The composition root

Open src/composition.ts first. It's the only file that says new Foo() for a service. Everything else receives its dependencies. Reading this file top to bottom tells you what the app is made of and how the pieces connect.

2. The FSM and phases

src/flow/fsm.ts is ~50 lines of finite state machine. Phases live in src/flow/phases/*.ts, one file each. Each phase has enter(ctx) and optional skip() / exit().

Read SpinPhase.ts — ten lines, three responsibilities: debit the bet, kick off reels + network in parallel, transition to stopSpin when the server responds. The client doesn't evaluate anything. It passes the server's answer along.

3. The wire types

src/domain/types.ts is every shape that crosses the network boundary: SpinRequest, SpinResponse, Grid, Winline. Deliberately small. No evaluateWin. No paytable. Biome rejects any import of pixi-reels, mobx, or outer layers from this folder.

4. The stores

src/state/RootStore.ts composes three stores:

  • BalanceStore — balance, bet, lastWin + actions to mutate.
  • DataStore — the last SpinResponse, decoded.
  • UIStore — spin/stop enabled flags, speed mode.

Presenters observe stores via MobX autorun. Phases mutate stores through action methods. No cross-store writes; a phase orchestrates any cross-store change.

5. The ticker

src/infrastructure/timing.ts defines Ticker with schedule, every, nextFrame. All three return a Disposable so the owner can cancel on phase exit without remembering handles.

Look at WinShowPhase.enter. It holds the win for 1.5 seconds via ctx.ticker.schedule(1500, () => ctx.fsm.transition('idle')). Not setTimeout. That's principle #2 in action.

6. The view boundary

src/presenters/ReelsPresenter.ts is the one class outside view/ that touches pixi-reels. Everything else goes through it. MainScene.ts lives in view/ and is where you wire ReelSetBuilder. Everywhere else, Biome rejects the import.

7. The HUD

index.html has three HTML elements: #spin, #stop, #balance. HUDPresenter binds MobX state to them via autorun. Swap for React/Vue/Svelte when you're ready — the presenter contract stays the same.

8. The agent setup

CLAUDE.md, AGENTS.md, and .claude/commands/ are committed in the template. Your agent loads them automatically (Claude Code) or via AGENTS.md / llms.txt (others). The principles are the same whether a human or an agent edits the code.

The big picture

SLOTPLATE · CLIENT UI plain DOM · optional React/Vue scenes Pixi application lifecycle · mounts pixi-reels flow · FSM + phase handlers idle → spin → stopSpin → winShow → idle owns GAME TIME · only writer to stores presenters ReelsPresenter · HUDPresenter state → view infrastructure Ticker · Network · AssetLoader · Analytics I/O boundary state · MobX RootStore = Balance + Data + UI domain · WIRE TYPES ONLY SpinRequest · SpinResponse · Grid · Winline config columns · rows · symbolIds (client-side only) ↓ import direction (enforced by Biome) SERVER (RGS) not slotplate paytable reelstrips RNG + spin eval RTP / math balance (truth) SpinResponse The client never computes these. It renders what the server said happened.
Everything you just read about, in one frame.

Where to go next