Multiplayer Games Design
I'm building a series of multiplayer games where players connect directly to each other via WebRTC (P2P), without using a server.
WebRTC Multiplayerβ
Players can start instantly by creating a host and letting others join, or by connecting to a friendβs hostβboth directly via WebRTC, regardless of distance.
In this setup, one client acts as both the server (or host) and a player. This host is responsible for the entire game state and logic. All other clients connect directly to this host. The host's GameLogic component receives actions from all players (including itself), computes the next game state, and then broadcasts the update to everyone.
The host (server) will have three main components: Network, GameLogic, and GameUI. In contrast, the other clients only need the Network and GameUI components.
- Network: Sends and receives player actions and game state updates.
- GameLogic (Host-only): Processes incoming actions to compute the next game state.
- GameUI: Renders the current game state and captures player input (actions).
Here are the implementation logic, 1 codebase handle both host and client cases.
This design applies on the current Bomb game. My next games will follow this closely.
Implementationβ
Message types from host to client, client to host.
// types.ts
export type GameClientMsg =
// client to host
| { type: 'join', pos: { x: number, y: number } }
| { type: 'action1', dataAction1: {...} }
| { type: 'action2', dataAction2: {...} }
// ...
export type GameHostMsg =
// host to client
| { type: 'joinedSuccess', id: number }
| { type: 'update1', dataUpdate1: {...} }
| { type: 'update2', dataUpdate2: {...} }
// ...
The key implementation logic here is the Network component. It receives the clientId and functions sendAll(msg), sendTo(clientId, msg) from the WebRTC layer.
GameNetworkβ
// Host only
sendToClients(msg: GameHostMsg) {
// update the UI itself
this.gameUI.handleHostMsg(msg)
// send to other clients
this.sendAll?.(JSON.stringify(msg))
}
// Host only
sendToClient(clientId: string, msg: GameHostMsg) {
if (clientId === this.clientId) {
// send to itself
this.gameUI.handleHostMsg(msg)
} else {
this.sendTo?.(clientId, JSON.stringify(msg))
}
}
// Both host and client
sendToHost(msg: GameClientMsg) {
if (this.gameLogic) {
// host sends to the its GameLogic component
this.gameLogic.handleClientMsg(this.clientId, msg)
} else {
// client sends to host over network
this.sendTo?.(this.hostId, JSON.stringify(msg))
}
}
// Both host and client
receiveMsg(fromClientId: string, data: string) {
if (this.gameLogic) {
// host receives msg from client
const msg: GameClientMsg = JSON.parse(data)
this.gameLogic.handleClientMsg(fromClientId, msg)
} else {
// client receives msg from host
const msg: GameHostMsg = JSON.parse(data)
this.gameUI.handleHostMsg(msg)
}
}
GameLogicβ
constructor(private gameNetwork: GameNetwork) {
// Running gameloop
setInterval(() => {
this.update()
}, GameLoopTime)
}
// receive action from a client
handleClientMsg(clientId: string, msg: GameClientMsg) {
switch (msg.type) {...}
}
// loop update
private update() {...}
GameUIβ
// receive game updates from host
handleHostMsg(msg: GameHostMsg) {
switch (msg.type) {...}
}
We can optimize network usage by encoding structured data into a binary format before sending it, and decoding it after receiving it.
WebRTC Over Central Serverβ
Below are the pros and cons of using WebRTC instead of a central server for multiplayer games.
Prosβ
- Single codebase: Faster to implement, test, and deploy.
- Decentralized hosting: Anyone can host a game at almost no cost, with virtually unlimited scalability.
- Low latency: Especially fast on the same local network.
- P2P live streaming: Supports streaming gameplay to millions through peer-to-peer connections.
Consβ
- Network restrictions: Some networks may block P2P connections.
- Cheating risk: Host may manipulate results or cheat on the leaderboard.
Featuresβ
- P2p multiplayers.
- P2p game streaming.
- Record and playback.
- AI integration (TODO):
- Live AI commentator.
- AI create games applying the p2p multiplayers structure.

