Turning a Socket.IO multiplayer prototype into a Net Forge game
Net Forge, the open-source multiplayer browser game engine built for AI coding agents, is published on npm under @netforge with source at github.com/netnetdev/net-forge.
Socket.IO is a library for "low-latency, bidirectional and event-based communication" between a client and a server, with rooms, broadcasting and automatic reconnection (Socket.IO docs), and it is a quick way to get two browsers talking. When a prototype outgrows hand-written events, move it to Net Forge: the room host steps one simulation, each browser predicts its own player with the same code, and your rules live in one declarative file.
npm create netforge@latest my-game -- --template arena --yes
cd my-game
npm run play -- --players 2Why prototypes move
A typical Socket.IO prototype broadcasts each player's position and lets every browser draw what it receives. That is the right way to start. As the game grows, the same questions come up: who decides a hit, how to stop a client from teleporting, how to hide the wait for the server, and how to keep two copies of the rules in step. Net Forge answers them with one design:
| In the prototype | In Net Forge |
|---|---|
socket.emit("move", { x, y }) | Sequence-numbered input commands (move, aim, buttons), quantized so client and server read identical values |
io.to(room).emit("state", players) | Quantized delta snapshots per player, 20 a second in local play |
| Rules in the client, the server, or both | Rules once, in the shared simulation, decided by the room |
| Drawing whatever arrives | The local player predicted and reconciled; others interpolated |
socket.join(roomId) | A room per game. Locally every tab joins the open room; on the hosted service a link with ?room=<id> joins a specific one |
| Reconnect handling | The room client's connection, on Colyseus |
1. Pick the template nearest your prototype
| Prototype | Template id |
|---|---|
Top-down shooter or .io arena | arena |
| Co-op against waves of enemies | survivor |
| Racing | racer |
| Platform race | platformer |
| Turn-based or card game | cards |
| Party minigames | party |
npm create netforge@latest my-game -- --template arena --yes2. Move rules, not messages
You do not port your events. Port what they meant: the things in your world and what happens to them, as declarations in src/game.ts.
import { prefab } from "@netforge/engine";
// What your server used to spawn and broadcast is now declared once.
prefab("drone", { model: "style.lowpoly.bat", health: 20, speed: 4.5, radius: 0.35, damage: 5, ai: "chase" });3. Check it with bots, then with people
npm test
npm run play -- --players 2npm test plays the game headless with bots and reports kills, rounds, softlocks and the cost per tick. The second line opens two tabs in the same room.
When to keep Socket.IO
Keep Socket.IO for the parts that are messages rather than simulation: chat, presence, notifications, dashboards or anything outside the game's rules. It also suits games whose state is small enough to send whole. Net Forge's src/game.ts may import only @netforge/engine, so side channels like chat belong in a service of your own.
FAQ
Is Socket.IO a WebSocket library?
Socket.IO can use WebSocket as its transport, but its docs say it is "NOT a WebSocket implementation": it adds metadata to each packet, so plain WebSocket clients and Socket.IO servers do not interoperate (Socket.IO docs).
What does Net Forge use for transport?
Rooms run on Colyseus over WebSocket. The local host in @netforge/kit-play uses @colyseus/core with @colyseus/ws-transport, and the browser connects with the Colyseus SDK.
Do I need to write prediction myself?
No. The template predicts the local player with the shared simulation and reconciles against the room's snapshots. See Netcode and prediction.
Can I keep my own server?
Net Forge games run on Net Forge rooms. If your game logic must stay in your own server, a room framework such as Colyseus is the better step up from Socket.IO (Net Forge and Colyseus).