Client vs server
The single most important boundary in a slot codebase.
slotplate is a client. The server is the source of truth — for the paytable, for the RNG, for the grid that comes back from a spin, and for the balance after it settles. This page is the definitive list of what belongs on which side.
The wire
Exactly one request-response pair crosses the boundary per round:
// src/domain/types.ts — the whole wire contract.
interface SpinRequest {
bet: number;
sessionId?: string;
}
interface SpinResponse {
grid: Grid; // what to show on the reels
totalWin: number; // what to credit the balance
winlines: Winline[]; // which cells won and for how much
teasingReels?: number[]; // which reels to slow down (optional)
bonus?: { id: string; payload: unknown }; // triggered bonus round
}
If a piece of state the client needs isn't in SpinResponse, the server
extends the response — not the client reverse-engineering it from grid.
Why
Regulated slot games get certified. The certification body runs the server's evaluator against millions of seeds, produces an RTP report, and signs it. If the client has its own evaluator, the two drift — even starting from the same code — and one ends up wrong. That's a live incident and a certification re-run.
The second reason is operational: when a win doesn't show correctly in the field, you need to know whether the bug is in the math or in the replay. If they're separate codebases with separate concerns, the answer takes minutes. If they're entangled, it takes days.
The mock server in slotplate
src/infrastructure/NetworkManager.ts ships with a MockNetworkManager
that returns a plausible SpinResponse with a random grid and no wins.
It exists so the client can boot during development without a real server.
Do not extend it with real math. If you want reproducible dev spins, run a real (even minimal) server alongside. The mock is a placeholder, not a sandbox.
Common temptations (all wrong)
"The client should know the paytable for tooltips."
Tooltips should come from the server too, or from a static asset the server publishes. Bundling the paytable into the client means shipping a new client to adjust a payout — exactly what you want to avoid. Serve paytable-as-data over an endpoint.
"We need optimistic win display for responsiveness."
No — the network response arrives before the reels finish landing. You're not waiting on the server; you're waiting on the reels. The order is: reels start → server responds → reels land on the server's grid → spotlight the server's winlines. No optimism needed.
"What about free spins? The server already committed, so the client should evaluate them."
No — each free spin is still a server round. The server is authoritative on whether the free-spin session continues, on the multiplier, on retriggers. The client plays them back one by one.
"For demo mode, the client must generate results."
Then run a demo server. Or add a server endpoint /demo/spin that uses a fixed
seed. The client still just replays. Never two evaluators.
What the client DOES own
- When to show things. The FSM phases sequence the reveal.
- How to animate. Reel behavior, win pulses, spotlight, bigwin scenes.
- Speed modes. Turbo / SuperTurbo skip phases — purely presentation.
- Skip / slam-stop. Cancelling animations to reveal early. State is unchanged.
- Input validation. Reject invalid bets before the request goes out.
- Optimistic balance display. Debit the bet locally for snappy feedback; the server's number is truth.
In short: the client owns presentation and timing; the server owns game state and outcomes. Keep the line clean.