The Multiplayer Framework Developers Love
Open-source, battle-tested, and built for real-time games. Free forever.
npm create colyseus-app@latest ./my-server Supports your favorite engine & platform
- Cheat-proof by design
- Built-in matchmaking
- Real-time sync that just works
- Scale from 10 to 10,000+ CCU
- Self-host or use Colyseus Cloud
- Use tools you already know
- Free forever (MIT License)
See It In Action
From Zero to Multiplayer
Define rooms, sync state, connect clients. A complete multiplayer server in just a few lines of code.
import { Room } from "colyseus";
class MyRoom extends Room { maxClients = 4; onCreate(options) {/* your code */} onAuth(client, options) {/* your code */} onJoin(client, options) {/* your code */} onLeave(client) {/* your code */} onDispose() {/* your code */}}import { defineServer, defineRoom } from "colyseus";import { MyRoom } from "./MyRoom";
export default defineServer({ rooms: { my_room: defineRoom(MyRoom), },});import { type, Schema, MapSchema, ArraySchema } from "@colyseus/schema";
export class Player extends Schema { @type("string") name: string; @type("number") position: number;}
export class MyState extends Schema { @type({map: Player}) players = new MapSchema<Player>(); @type(["number"]) board = new ArraySchema<number>([0, 0, 0, 0, 0, 0, 0, 0, 0]); @type("string") currentTurn: string;}import { Room } from "colyseus";import { MyState } from "./MyState";
export class MyRoom extends Room { state = new MyState();
onCreate() { // room has been created }
onJoin(client, options) { // new client joined the room this.state.players.set(client.sessionId, new Player()); this.state.currentTurn = client.sessionId; }
onLeave() { // client left the room this.state.players.delete(client.sessionId); }}import { Client } from "@colyseus/sdk";
// Initialize SDKconst client = new Client("https://0.0.0.0:2567");
// Connect to a roomconst room = await client.joinOrCreate("my_room");import { Client, Callbacks } from "@colyseus/sdk";
const client = new Client("https://0.0.0.0:2567");const room = await client.joinOrCreate("my_room", { /* client options */});
const callbacks = Callbacks.get(room);
// on player added/joincallbacks.onAdd("players", (player, sessionId) => { console.log("Player added!", player, sessionId); // on player "position" change callbacks.listen(player, "position", (position, previousPosition) => { console.log("player position changed!", { position, previousPosition }); });});
// on player removed/leftcallbacks.onRemove("players", (player, sessionId) => { console.log("Player removed!", player, sessionId);});
// on "board" changecallbacks.onChange("board", (value, index) => { console.log("board item changed to", value, "at", index);});
// on "currentTurn" changecallbacks.listen("currentTurn", (currentTurn, previousValue) => { console.log("currentTurn:", currentTurn); console.log("currentPlayer:", room.state.players.get(currentTurn));});Define your room
Rooms are the core building block. Each room runs your game logic, handles connections, and manages state. Define lifecycle hooks for when players join, leave, or send messages.
Expose for matchmaking
Register your room type with a single line. Colyseus handles the rest — creating new instances on demand, filling them up, and routing players to the right one.
Define your state
Define game state using decorated schema classes. Colyseus tracks changes and syncs them to all clients with binary delta compression.
Mutate state
Just modify your state objects like regular JavaScript. Colyseus detects changes and broadcasts only the deltas to connected clients.
Connect from the client
Connect to your server and join a room with a few lines of code. If no room is available, the server creates one automatically.
Listen to state changes
React to state changes with fine-grained callbacks. Know exactly when players join, leave, or any state updates.
Define your room
Rooms are the core building block. Define lifecycle hooks for when players join, leave, or send messages.
import { Room } from "colyseus";
class MyRoom extends Room { maxClients = 4; onCreate(options) {/* your code */} onAuth(client, options) {/* your code */} onJoin(client, options) {/* your code */} onLeave(client) {/* your code */} onDispose() {/* your code */}}Expose for matchmaking
Register your room type with a single line. Colyseus handles the rest.
import { defineServer, defineRoom } from "colyseus";import { MyRoom } from "./MyRoom";
export default defineServer({ rooms: { my_room: defineRoom(MyRoom), },});Define your state
Define game state using decorated schema classes. Colyseus tracks and syncs changes automatically.
import { type, Schema, MapSchema, ArraySchema } from "@colyseus/schema";
export class Player extends Schema { @type("string") name: string; @type("number") position: number;}
export class MyState extends Schema { @type({map: Player}) players = new MapSchema<Player>(); @type(["number"]) board = new ArraySchema<number>([0, 0, 0, 0, 0, 0, 0, 0, 0]); @type("string") currentTurn: string;}Mutate state
Just modify your state objects. Colyseus detects changes and broadcasts deltas.
import { Room } from "colyseus";import { MyState } from "./MyState";
export class MyRoom extends Room { state = new MyState();
onCreate() { // room has been created }
onJoin(client, options) { // new client joined the room this.state.players.set(client.sessionId, new Player()); this.state.currentTurn = client.sessionId; }
onLeave() { // client left the room this.state.players.delete(client.sessionId); }}Connect from the client
Join a room with a few lines of code. If none is available, the server creates one.
import { Client } from "@colyseus/sdk";
// Initialize SDKconst client = new Client("https://0.0.0.0:2567");
// Connect to a roomconst room = await client.joinOrCreate("my_room");Listen to state changes
React to state changes with fine-grained callbacks.
import { Client, Callbacks } from "@colyseus/sdk";
const client = new Client("https://0.0.0.0:2567");const room = await client.joinOrCreate("my_room", { /* client options */});
const callbacks = Callbacks.get(room);
// on player added/joincallbacks.onAdd("players", (player, sessionId) => { console.log("Player added!", player, sessionId); // on player "position" change callbacks.listen(player, "position", (position, previousPosition) => { console.log("player position changed!", { position, previousPosition }); });});
// on player removed/leftcallbacks.onRemove("players", (player, sessionId) => { console.log("Player removed!", player, sessionId);});
// on "board" changecallbacks.onChange("board", (value, index) => { console.log("board item changed to", value, "at", index);});
// on "currentTurn" changecallbacks.listen("currentTurn", (currentTurn, previousValue) => { console.log("currentTurn:", currentTurn); console.log("currentPlayer:", room.state.players.get(currentTurn));});Client SDKs
Build on Any Platform
Connect to your Colyseus server from any engine, framework, or language. State synchronization works the same everywhere.
import { Client, Callbacks } from "@colyseus/sdk";
const client = new Client("https://your-server.com");const room = await client.joinOrCreate("game_room");const callbacks = Callbacks.get(room);
callbacks.onAdd("players", (player, key) => { addSpriteForPlayer(player); callbacks.onChange(player, () => moveSprite(key, player.x, player.y));});
callbacks.onRemove("players", (player, key) => removeSpriteForPlayer(key));
room.send("move", { x: 10, y: 20 });npm install @colyseus/sdk import { Client } from "@colyseus/sdk";import { useRoom, useRoomState } from "@colyseus/react";
const client = new Client("https://your-server.com");
function Game() { const { room, isConnecting } = useRoom( () => client.joinOrCreate("game_room"), ); const state = useRoomState(room);
if (isConnecting) return <p>Connecting...</p>;
return ( <ul> {state?.players.map((player, key) => ( <li key={key}>Player at {player.x}, {player.y}</li> ))} </ul> );}npm install @colyseus/react using Colyseus;using Colyseus.Schema;
var client = new Client("https://your-server.com");var room = await client.JoinOrCreate<GameState>("game_room");var callbacks = Callbacks.Get(room);
callbacks.OnAdd(state => state.players, (sessionId, player) => { AddSpriteForPlayer(player); callbacks.OnChange(player, () => MoveSprite(sessionId, player.x, player.y));});
callbacks.OnRemove(state => state.players, (sessionId, player) => RemoveSpriteForPlayer(sessionId));
await room.Send("move", new { x = 10, y = 20 });var client: ColyseusClient = Colyseus.create_client()client.set_endpoint("wss://your-server.com")var room: ColyseusRoom = client.join_or_create("game_room")
room.joined.connect(func(): var callbacks = Colyseus.callbacks(room)
callbacks.on_add("players", func(player, key): add_sprite_for_player(player) callbacks.on_change(player, func(): move_sprite(key, player.x, player.y) ) )
callbacks.on_remove("players", func(player, key): remove_sprite_for_player(key) )
room.send("move", {"x": 10, "y": 20}))client = new ColyseusClient("https://your-server.com");room = client.join_or_create("game_room");callbacks = ColyseusCallbacks(room);
callbacks.on_add("players", function(player, key) { add_sprite_for_player(player); callbacks.on_change(player, function() { move_sprite(key, player.x, player.y); });});
callbacks.on_remove("players", function(player, key) { remove_sprite_for_player(key);});
room.send("move", { x: 10, y: 20 });local ColyseusSDK = require "colyseus.sdk"
local client = ColyseusSDK.Client("https://your-server.com")client:join_or_create("game_room", function(room) local callbacks = ColyseusSDK.callbacks(room)
callbacks:on_add("players", function(player, key) add_sprite_for_player(player) callbacks:on_change(player, function() move_sprite(key, player.x, player.y) end) end)
callbacks:on_remove("players", function(player, key) remove_sprite_for_player(key) end)
room:send("move", { x = 10, y = 20 })end)import io.colyseus.Client;import io.colyseus.schema.Callbacks;
var client = new Client("https://your-server.com");client.joinOrCreate("game_room", [], MyRoomState, function(err, room) { var callbacks = Callbacks.get(room);
callbacks.onAdd("players", (player, key) -> { addSpriteForPlayer(player); callbacks.onChange(player, () -> moveSprite(key, player.x, player.y)); });
callbacks.onRemove("players", (player, key) -> removeSpriteForPlayer(key));
room.send("move", {x: 10, y: 20});});haxelib install colyseus #include "colyseus.h"
colyseus_client_t* client = colyseus_client_new("https://your-server.com");colyseus_room_t* room = colyseus_join_or_create(client, "game_room");colyseus_callbacks_t* callbacks = colyseus_callbacks_get(room);
void on_player_add(colyseus_state_t* player, const char* key) { add_sprite_for_player(player); colyseus_on_change(callbacks, player, on_player_change, (void*)key);}
void on_player_change(colyseus_state_t* player, void* ctx) { float x = colyseus_get_float(player, "x"); float y = colyseus_get_float(player, "y"); move_sprite((const char*)ctx, x, y);}
colyseus_on_add(callbacks, "players", on_player_add, NULL);colyseus_on_remove(callbacks, "players", on_player_remove, NULL);colyseus_room_send(room, "move", data, data_len);const client = new Colyseus.Client("https://your-server.com");const room = await client.joinOrCreate("game_room");const callbacks = Colyseus.Callbacks.get(room);
callbacks.onAdd("players", (player, key) => { const sprite = runtime.objects.Player.createInstance("Game", player.x, player.y); callbacks.onChange(player, () => { sprite.x = player.x; sprite.y = player.y; });});
callbacks.onRemove("players", (player, key) => { // destroy sprite instance});
room.send("move", { x: 10, y: 20 });Under the Hood
Architecture / Stack
A solid stack so you can focus on your game instead of re-inventing the wheel.
Node.js Runtime
Asynchronous event-driven JavaScript runtime, designed to build scalable network applications.
Networking Transport
WebSockets as transport layer, with WebTransport support in our public roadmap.
Room-Based System
On-demand, stateful rooms are spawned per client request.
Message Serialization
Binary delta serialization for state sync, and the fastest MessagePack serializer for everything else.
Web / API Routes
Define your own custom API routes via Express or better-call.
Database / Auth
Bring your favorite Node.js tools. You choose which database or authentication provider to use.
Scale with Confidence
Scalability
Simple yet powerful scaling strategy
- Distribute rooms across servers and/or processes
- Allow cluster-wide match-making
- Leverage all your available CPUs
- Connect clients directly to their requested rooms
- Stateful processes keep track of their own rooms
Developer Experience
Tooling
Built-in tools that accompany you from development to production.
Client-side Playground
Create client connections and inspect data exchanged with the server.
Realtime Room Inspector
View and inspect the list of rooms spawned by the server.
Unit Testing
Utility methods for testing your rooms with your favorite testing framework.
Load Testing
Evaluate how your server behaves under high traffic.
Node.js ecosystem
Bring your favorite tools to your Colyseus server.
Become a Sponsor
The sustainability of Colyseus relies on Colyseus Cloud subscriptions and sponsorships.
If you benefit from the project but are not yet using Colyseus Cloud, please consider sponsoring it.




