The Multiplayer Framework Developers Love

Open-source, battle-tested, and built for real-time games. Free forever.

$ npm create colyseus-app@latest ./my-server

Requires Node.js

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.

Server-side

Define your room

Rooms are the core building block. Define lifecycle hooks for when players join, leave, or send messages.

MyRoom.ts Server
MyRoom.ts
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 */}
}
Server-side

Expose for matchmaking

Register your room type with a single line. Colyseus handles the rest.

app.config.ts Server
app.config.ts
import { defineServer, defineRoom } from "colyseus";
import { MyRoom } from "./MyRoom";
export default defineServer({
rooms: {
my_room: defineRoom(MyRoom),
},
});
Server-side

Define your state

Define game state using decorated schema classes. Colyseus tracks and syncs changes automatically.

MyState.ts Server
MyState.ts
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;
}
Server-side

Mutate state

Just modify your state objects. Colyseus detects changes and broadcasts deltas.

MyRoom.ts Server
MyRoom.ts
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);
}
}
Client-side

Connect from the client

Join a room with a few lines of code. If none is available, the server creates one.

client.ts Client
client.ts
import { Client } from "@colyseus/sdk";
// Initialize SDK
const client = new Client("https://0.0.0.0:2567");
// Connect to a room
const room = await client.joinOrCreate("my_room");
Client-side

Listen to state changes

React to state changes with fine-grained callbacks.

client.ts Client
client.ts
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/join
callbacks.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/left
callbacks.onRemove("players", (player, sessionId) => {
console.log("Player removed!", player, sessionId);
});
// on "board" change
callbacks.onChange("board", (value, index) => {
console.log("board item changed to", value, "at", index);
});
// on "currentTurn" change
callbacks.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.

client.ts Client
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
Game.tsx Client
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
GameClient.cs Client
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 });
client.gd Client
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})
)
obj_client.gml Client
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 });
client.lua Client
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)
Client.hx Client
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
client.c Client
#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);
game.js Client
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 });

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
Scalability

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.