// TECH //
How we build online multiplayer browser games
The choices behind every real-time multiplayer game we ship to the browser: which network model, what the servers do, how strangers find each other and how friends get in with one link.
An online multiplayer game in a browser tab has the same problems as one on a console, with fewer tools to solve them: no installer, no raw sockets beyond WebSockets and WebRTC, and a player who closes the tab the moment something feels slow. This is how we approach it at MobX Games, at the level of decisions rather than any one game, and why the answers come out the way they do.
First decision: who owns the truth?
Every multiplayer game has to answer one question before anything else: when two players disagree about what happened, whose version wins? There are three common answers, and we pick per game rather than per studio.
- An authoritative server. One machine runs the real game, players send inputs, and the server sends back what happened. It is the right answer when hidden information or cheating matters, and the most expensive one, because the server has to simulate every match, every tick, for as long as it lasts.
- Deterministic lockstep. Every player runs the same simulation and only orders cross the network. If every machine starts from the same state and applies the same orders at the same tick, every machine computes the same result. A battle with hundreds of units costs a few hundred bytes per second, because nobody ever sends a unit position.
- Client prediction with rollback. Each player's game assumes the opponent kept doing what they did last and simulates ahead at once. When the real input arrives and disagrees, the game rewinds to the last agreed frame and replays the difference. Your own controls never wait for the network, which is what a fast action game with few moving objects needs.
Strategy games with many units suit lockstep, because the traffic scales with what players do, not with what is on screen. Fast physics games for two suit rollback, because the delay you feel on your own controls matters more than anything else. We reach for a full authoritative server least often in the browser, since it turns every match into a running cost.
A relay, not a game server
Lockstep and rollback both leave the server with very little to do: it only has to pass messages between players in the same room. So our backend is a relay, and a relay fits serverless hosting well. Players connect over a WebSocket to an AWS API Gateway endpoint; a small Lambda function receives each message, looks up the other players in that room in a DynamoDB table and forwards it. There is no game server sitting idle at four in the morning waiting for someone to play, and no machine to patch.
The trade is that a serverless relay bills per message, so the shape of the traffic is a cost decision as much as a latency one. Sending fewer, slightly larger packets and repeating recent inputs inside each one, so a lost packet repairs itself, keeps the bill in proportion to the number of players. Where the network allows it, the game also opens a direct WebRTC data channel between the players, unordered and without retransmissions, because a late input is worth nothing. The relay stays as the floor: if the direct channel never opens, the match plays on through the relay instead of failing.
Regions and matchmaking
The relay runs in several AWS regions, because the speed of light across an ocean is a delay no code can remove. A game picks its region either by guessing from your time zone, with a setting to override it, or by opening a connection to each region and keeping whichever answers first.
Matchmaking tries the dull option first. If a public lobby is waiting with a free seat and a live host, you join it straight away: landing in a real match beats waiting for a perfectly fair one. Otherwise you enter a queue whose acceptable skill gap widens the longer you wait, and two players pair only when both of their windows cover the gap, so a long waiter is not handed a newcomer they would crush. The server keeps no timers for this. Your client simply asks again every few seconds, which keeps the matchmaker stateless and cheap. When a pair is found, both clients start the match at a moment they have agreed on, not the moment each one happened to hear about it.
None of that conjures an opponent at quiet hours. Matchmaking takes seconds when someone else is searching and is a wait when nobody is, which is why every one of our multiplayer games also lets you play a friend directly.
Lobby codes and invite links
A private match starts with a short lobby code, short enough to read out loud across a room or type on a phone. The host's game also offers a Copy invite button that puts a link with the code in it on the clipboard. Whoever opens that link lands on the game's page on mobx.games, the page passes the code on into the game, and the cover is skipped, because a link from a friend is a request to join, not an invitation to read.
Reconnects and determinism checks
Lockstep only works if every machine really does compute the same thing, and determinism is a rule the code obeys, not a hope. Nothing inside the simulation reads the system clock or an unseeded random source. Dice rolls come from a seeded generator that every player advances in the same order, the simulation runs on fixed ticks rather than on frame time, and anything that loops over a collection loops in a stable order. On top of that, every client hashes its game state at regular intervals and compares the result with the other players. A mismatch is caught the tick it shows up, instead of two different matches quietly playing out on two screens.
Connections drop, especially on phones. Because the bot opponent runs inside the same deterministic simulation and needs no network traffic, a player who disconnects can be handed to the bot on every machine at the same tick, and the match goes on for everyone else. A player who comes back fetches a snapshot of the current state and takes their seat again. In a rollback game the repeated inputs in every packet cover short gaps without anyone noticing.
Accounts that start anonymous
Nobody should have to sign up before their first match. The first time a game needs an identity, our accounts service creates an anonymous device account: a player id and a token, no email and no password. Ratings, friends and progress hang off that id from the first game. If you want the same account on your phone and your laptop, you add an email address and confirm a six digit code, and the anonymous account becomes a permanent one with nothing lost. The token lives in a cookie on the parent domain, so every game on its own mobx.games subdomain sees the same player without asking again.
Running inside a portal iframe
On mobx.games each game runs in a sandboxed iframe on its own subdomain. A separate origin means the game cannot read the page around it and the page cannot read the game, and each game keeps its saves and cache apart from every other. The frame is granted exactly what a multiplayer game needs: fullscreen, pointer lock, autoplay for sound, and clipboard access so the Copy invite button works inside the frame.
Game and page talk through a small postMessage contract. The game asks the page to open the sign-in window, because the page owns sign-in; the page tells the game when you sign in or out, and the game looks up its player again with no reload. Both sides check where a message comes from and send only to the exact origin they expect.
Discord Activities
A Discord Activity is a web game running inside Discord, in a voice channel or a chat, which makes it a natural home for multiplayer: the people you want to play with are already in the room. The same build that runs on mobx.games can serve as the Activity. Discord loads it through its own proxy, so the game recognises from its address that it runs inside Discord and only then loads Discord's Embedded App SDK; every other build never downloads it. Sign-in becomes a Discord authorization whose code a small serverless function exchanges for a token, so the app secret never reaches the browser, and invites go through Discord's own invite dialog. Everything underneath, the relay, the regions and the simulation, stays exactly the same.
What we would tell someone starting out
- Pick the network model from the game, not from habit: many units point to lockstep, two fast players to rollback.
- Make the server as simple as the game allows. A relay is cheap, stateless and easy to run in several regions.
- Treat determinism as a rule with a check behind it, and hash your state.
- Let people play first and sign up later, and make a friend's link the shortest path into a match.