Ever wondered how those satisfying multiplayer games work? You know, the ones where you can see other players moving around in real-time, placing pixels on a shared canvas, or battling snakes together? Today, I'll take you behind the scenes of building two real-time multiplayer games: a collaborative painting app and a competitive Snake game.
Spoiler alert: it's way easier than you think, and incredibly fun to build!
The "Aha!" Moment: WebSockets
Traditional web communication is like sending letters back and forth. The client (your browser) sends a request, the server responds, and then... silence. But real-time multiplayer games need constant chatter—like having a phone conversation instead of exchanging postcards.
Enter WebSockets—the magic that makes it all possible. Think of WebSockets as opening a persistent tunnel between your browser and the server. Once that tunnel is open, both sides can send messages instantly, creating that real-time experience.
"The moment you see another player's snake moving in real-time while you're playing—that's WebSockets working their magic."
Our Tech Stack: Simple But Powerful
Here's what powers our multiplayer games:
Server Side: Node.js + Socket.IO
Node.js runs our server using JavaScript (the same language as the frontend!). No context switching, no mental gear changes—just JavaScript everywhere.
Socket.IO is like WebSockets with superpowers. It handles all the messy networking stuff automatically:
- Automatic reconnection when your WiFi hiccups
- Fallback to regular HTTP when WebSockets aren't available
- Built-in room management for organizing players
- Cross-browser compatibility (even works on your uncle's ancient IE)
// Our basic server setup
const io = new Server(server, {
cors: { origin: "https://birdsynth.com" }
});
io.on('connection', (socket) => {
console.log('Player joined!');
socket.on('placePixel', (data) => {
// Broadcast pixel to all other players
socket.broadcast.emit('pixelUpdate', data);
});
});
Client Side: Vanilla JavaScript + HTML5 Canvas
No fancy frameworks needed! We're using:
- HTML5 Canvas for rendering our games
- Socket.IO Client for talking to the server
- ES6 Modules for clean, organized code
Game #1: The Collaborative Painting Canvas
This was the more zen of our two games. Picture this: a shared 64×64 pixel canvas where anyone can drop in and start painting. No accounts, no friction—just pure creative collaboration.
The Architecture
The painting game uses what I call the "broadcast pattern":
- Player clicks on the canvas
- Client immediately draws the pixel locally (for responsiveness)
- Client sends pixel data to server
- Server validates and broadcasts to all other players
- Server saves canvas state to disk every 30 seconds
// Client-side pixel placement
placePixel(x, y) {
// Instant local feedback
this.paintingCanvas[y][x] = this.currentColor;
this.drawPixel(x, y, this.currentColor);
// Tell everyone else
this.socket.emit('placePixel', {
x: x, y: y, color: this.currentColor
});
}
The beauty? No complex state synchronization needed. Each pixel placement is independent, and the server acts as the source of truth.
Persistence: Because Art Should Last
We store the entire canvas as a JSON file—simple and effective. When players join, they get the current canvas state. When they leave, their artwork remains for others to build upon.
// Canvas persistence (server-side)
function savePaintingCanvas() {
fs.writeFileSync('canvas.json', JSON.stringify(paintingCanvas));
}
// Auto-save every 30 seconds
setInterval(savePaintingCanvas, 30000);
Game #2: Multiplayer Snake - The Real Challenge
Snake ramped up the complexity significantly. Unlike painting, where conflicts are rare, Snake has:
- Fast-paced gameplay (150ms ticks)
- Collision detection between players
- Shared resources (food)
- Win/lose conditions
The Authoritative Server Pattern
For Snake, we needed an authoritative server—the server makes all the important decisions:
- Clients send input (arrow key presses)
- Server processes all game logic
- Server broadcasts the complete game state
- Clients render what the server tells them
This prevents cheating and keeps all players perfectly synchronized.
// Server-side game loop
function updateGame(roomId) {
const room = rooms.get(roomId);
// Move all snakes
for (const [playerId, snake] of Object.entries(room.snakes)) {
const head = {
x: snake.body[0].x + snake.direction.x,
y: snake.body[0].y + snake.direction.y
};
// Check collisions, update positions, handle food...
}
// Broadcast complete game state to all players
io.to(roomId).emit('gameUpdate', room.gameState);
}
Matchmaking: Keeping It Simple
Our matchmaking is elegantly simple:
- Player joins → finds first available room with 1 player
- No available room → creates new room and waits
- Room fills up → game starts automatically
No complex lobbies, no waiting rooms—just instant action.
The Fun Challenges We Solved
HTTPS + WebSockets = Headaches
Modern browsers require secure WebSockets (wss://) when serving over HTTPS. Our solution? Nginx as a reverse proxy:
location /socket.io/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
This lets our Node.js server run on plain HTTP locally while exposing secure WebSockets to the world.
Making Pixels Look Crisp
Getting pixel-perfect rendering on modern browsers requires some CSS magic:
#painting-canvas {
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
}
Without this, your beautiful pixel art turns into a blurry mess!
What Made This Special
The magic isn't in any single technology—it's in how they work together:
- Socket.IO handles the complex networking
- Node.js keeps everything in JavaScript
- HTML5 Canvas gives us pixel-perfect control
- Simple architecture makes debugging a breeze
Try It Yourself!
Want to build your own multiplayer game? Start simple:
- Set up a basic Socket.IO server
- Create a simple client that can send messages
- Add basic state synchronization
- Expand from there!
The best part about real-time multiplayer games? You get to experience the magic of seeing your creation come alive when other players join. There's something deeply satisfying about watching two snakes chase food together or seeing collaborative pixel art emerge on your canvas.
Ready to dive in? Check out our Multiplayer Snake and Collaborative Painting games to see these concepts in action. Then start building your own—the web is waiting for your multiplayer masterpiece!
Want to see the code? All the games mentioned in this post are running live at birdsynth.com/games. Happy coding!