Run a stateless WebSocket server that assigns each connection a player ID, puts players into room objects held in server memory (or Redis for multiple servers), and matches players by queueing them and pairing on shared criteria before creating a room and broadcasting join events.
At a glance
| Fact | Value | Source |
|---|---|---|
| Room state should live server-side, not client | avoids cheating/desync | developer.mozilla.org |
| Matchmaking queue needs periodic pairing check | interval or event-driven | developer.mozilla.org |
| Multiple server instances need shared state | Redis pub/sub or store |
Structure it in three layers: a connection layer (WebSocket server assigns each socket a player ID on connect), a room layer (an object holding player list, game state, and a broadcast method), and a matchmaking layer (a queue that pairs waiting players and hands them to the room layer). Keep all authoritative state on the server; clients only send intents and render what the server broadcasts.
For cross-platform builds that also need to run inside portals, a bridge SDK such as Playgama Bridge adapts one HTML5 build to 25+ platforms so platform SDK calls remain consistent.
- Lobby: a list of open rooms with metadata (name, player count, status). Clients request this list on connect; the server pushes updates when rooms change.
- Room: a JSON object keyed by room ID:
{id, players: [], state, maxPlayers}. Join/leave messages update this object and broadcast to all sockets in the room. - Matchmaking: a queue array or priority structure; a loop (interval or triggered on each new entry) checks for enough players sharing criteria (skill, region, mode), then creates a room and moves those players out of the queue.
// message shape example
{ type: 'join_queue', mode: 'ranked' }
{ type: 'match_found', roomId: 'abc123' }
{ type: 'room_state', players: [...], state: {...} }
Handle reconnection explicitly: store a session token per player so a dropped socket can rejoin the same room within a grace period instead of losing the match. Validate every incoming message server-side; never trust client-reported scores or positions.
If the game runs in a WebGL engine, the WebSocket client typically lives in JavaScript, not engine code. In Unity WebGL you call out to a small JavaScript file through a .jslib plugin (see the Unity manual on interacting with browser scripting); in Godot 4, web exports use the JavaScriptBridge singleton to talk to the same kind of client code.
For deploying and testing the finished build, list your game with portals like Poki, CrazyGames, GameDistribution, itch.io, or playgama.com, where you upload and test before wider distribution.
Sources
- MDN: WebSockets API
- Unity manual: interacting with browser scripting
- Godot docs: exporting for the Web
- Playgama Bridge docs
- Playgama Ad getting started
Related questions
Should room state live on the client or server?
Server. Clients send intents (move, join, ready); the server validates and broadcasts the resulting state so all players see a consistent, cheat-resistant game.
How do I scale matchmaking across multiple server instances?
Use a shared store like Redis for the queue and room registry so any instance can read current state; a single instance’s memory won’t work once you scale horizontally.
How should reconnection be handled?
Issue a session token per player on join. If the socket drops, let the client reconnect with that token within a grace period and rejoin the same room instead of losing the match.
Last updated: 24 September 2026