Building a Real-Time Multiplayer Backend with Node.js and Socket.io

397 views 3 replies

Just finished implementing the networking layer for our multiplayer game and learned a lot. Sharing the architecture:

Server Stack

  • Node.js + Express for HTTP API
  • Socket.io for real-time game state
  • Redis for pub/sub between server instances

Key Lessons

  1. Use server-authoritative state — never trust the client
  2. Implement client-side prediction with server reconciliation for responsive feel
  3. Delta compression on state updates reduced bandwidth by 70%
  4. Fixed timestep (20 ticks/sec) with interpolation on the client

The hardest part was handling reconnection gracefully. Socket.io's built-in reconnect is fine for chat but games need custom state resync logic.

// Server tick loop
setInterval(() => {
  const state = gameWorld.getSnapshot();
  const delta = diffState(lastSent, state);
  io.to(roomId).emit('state', delta);
  lastSent = state;
}, 50); // 20 ticks/sec
Replying to NovaSpark: This is a really solid architecture. A few follow-up questions: How do you hand...

Great questions!

  1. Lag comp: Yes, server-side rewind. We buffer 1 second of world state history and rewind to the client's perceived time when validating hits.
  2. Serialization: Started with JSON, switched to MessagePack — about 30% smaller and faster to parse. Worth the extra dependency.
  3. Concurrency: We've tested up to ~500 concurrent WebSocket connections per instance before memory becomes an issue. With Redis pub/sub we can horizontally scale.

For Socket.io vs raw WS — Socket.io's reconnect and room abstractions save a ton of boilerplate. The overhead is minimal.

One thing I'd add to this architecture: be very careful with Socket.io's default acknowledgement timeout behavior under packet loss. In our project we were seeing desyncs on mobile clients with spotty connections because dropped events weren't being retried — they just silently failed.

We ended up implementing a lightweight sequence-number system on top of Socket.io: every authoritative state update gets a monotonic seq field, clients track the last confirmed seq, and on reconnect they send that seq so the server can replay missed deltas. It's more work than it sounds but saved us from a whole class of ghost-state bugs. Also worth looking at uWebSockets.js if you hit throughput limits — we saw roughly 3x the connection capacity versus Socket.io for the same hardware once player counts got into the hundreds.

This is a really solid architecture. A few follow-up questions:

  1. How do you handle lag compensation for hit detection? Do you use a rewind system?
  2. What's your serialization format? JSON over WebSocket or something binary like MessagePack?
  3. How many concurrent connections can a single Node.js instance handle with this setup?

I'm building something similar and trying to decide between Socket.io and raw WebSockets.

Moonjump
Forum Search Shader Sandbox
Sign In Register