20261 min readSolo Developer
Pictobattle
A real-time multiplayer Pictionary game with canvas drawing, live guessing, and horizontal scaling — built as a pnpm monorepo.
Problem
A turn-based drawing game (Pictionary like) sounds simple, but the hardest part isn’t drawing — it’s keeping every player in sync in real time. Strokes, guesses, scores, and turn state all need to stay consistent across clients, and a single dropped connection can break the game if the state lives only in memory.
Solution
- Structured the project as a pnpm monorepo with three packages: a React frontend, a Node.js/Express backend, and a shared types package. This kept the API contract between client and server in sync without manual coordination — change a type once, and both sides pick it up.
- Chose Socket.io for the real-time layer. Each room runs as an event-driven game loop where drawing, guessing, and scoring events flow through the same channel. The 2-minute turn timer and 5-round structure are enforced server-side, so clients can’t desync.
- Built the drawing canvas on HTML5 Canvas with lightweight event batching. Every stroke is broadcast to other players as a stream of coordinate events, not as a full image, keeping latency low and bandwidth minimal even with several players drawing simultaneously.
- Added a reconnect strategy: when a player drops or refreshes, the server replays the current game state so they rejoin mid-round without losing context. No one gets kicked for a spotty connection.
- Redis Pub/Sub for horizontal scaling: Eoom events fan out across instances, so the game can run beyond a single process when it needs to.
Result
- Smooth multiplayer with no perceptible lag on strokes or guesses, even with a full room.
- The monorepo pattern meant adding features like avatars or live scoreboards touched both client and server in a single, type-checked pass.
- Docker containers of server and client for easier deployment.