SP slotplate
Architecture

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.

CLIENT · slotplate presentation + flow rendering reels animations, timing, skip HUD, bet input, sound FSM phase orchestration optimistic balance display request spin from server paytable logic RNG / stop positions win evaluation RTP / math certification balance truth SERVER · RGS math + truth paytable + payouts RNG + reelstrip lookup grid selection per spin win evaluation anticipation trigger rule bonus trigger decision balance authoritative state session lifecycle RTP certification rendering / animation sound / HUD / speed modes SpinRequest { bet } SpinResponse grid, winlines, totalWin, teasingReels
Checkmarks = owned here. Strikethroughs = must not be here.

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.