A tour of how slotplate is put together — one diagram per concept, with the rationale on the side.
1. Who owns what: client vs server
This is the first diagram to read. slotplate is a client — it does
not own the math. Confusing this split is how slot codebases end up with
parallel evaluators (one on the server, one on the client) that drift until
certification fails.
Client animates. Server computes. Between them flows exactly one pair of messages per round.
2. The layer stack
Inside the client, layers import downward only. Biome enforces the boundaries — you
can't import pixi-reels from a store, you can't import MobX from domain types,
you can't reach from view/ back into flow/.
The orange block (FSM) is privileged — it's the only writer to the stores. Everything else reads.
3. Unidirectional data flow
Data moves one way around the loop. Input triggers an FSM transition. The transition
mutates state through a store action. Presenters observe state changes and drive the view.
The view runs, and when it finishes (reels land, animations complete) that resolution
feeds back to the FSM — which transitions again.
4. The finite state machine
A slot round is a finite state machine. slotplate makes the states explicit.
Each named phase is a file in src/flow/phases/. The FSM runs exactly one
phase at a time. Transitions are explicit calls — never flag-based.
idle → spin → stopSpin → winShow → idle. Bonus games nest as sub-FSMs inside their own phase.
5. The spin lifecycle, in sequence
The same flow as a sequence diagram — who calls whom, when. Time flows top to bottom.
The par block (marked with a dashed rectangle) is the critical bit: reels
start rolling and the network request goes out in parallel. Whichever finishes first,
the FSM is ready for it.
6. Timing: why no setTimeout
The quiet architectural detail that bites every slot codebase eventually.
setTimeout runs on the platform clock and keeps firing in backgrounded tabs
— even while Pixi's ticker pauses. slotplate uses gsap.delayedCall on a
GSAP ticker that's synced to app.ticker. When Pixi pauses, game timing
pauses with it.
Left: the common bug. Right: the fix. All scheduled game-time calls go through the Ticker abstraction in src/infrastructure/timing.ts.
Where to next
Client vs server — the boundary in more detail, with concrete dos and don'ts.