Client SDK
The Client SDK provides everything you need to connect to a Colyseus server from your game or application.
Client Setup
The Client instance is your entry point to connect to the server.
import { Client } from "@colyseus/sdk";
const client = new Client("http://localhost:2567");
// ... with full-stack type safety (optional)
import type { server } from "../../server/src/app.config.ts";
const client = new Client<typeof server>("http://localhost:2567");Joining Rooms
Once you have a client, you can join rooms. Choose the method that best fits your matchmaking needs.
Join or Create (Recommended)
The most common way to connect. Joins an existing room if available, or creates a new one.
try {
const room = await client.joinOrCreate("battle", {/* options */});
console.log("joined successfully", room);
} catch (e) {
console.error("join error", e);
}Locked or private rooms are ignored by this method.
Other Join Methods
These methods use the same pattern as joinOrCreate — replace the method name accordingly.
| Method | Signature | Description |
|---|---|---|
create | client.create(roomName, options) | Always creates a new room, even if others exist. |
join | client.join(roomName, options) | Joins an existing room. Fails if none available. Locked/private rooms are ignored. |
joinById | client.joinById(roomId, options) | Joins a specific room by its unique ID. Private rooms can be joined by ID. Useful for invite links. |
consumeSeatReservation | client.consumeSeatReservation(reservation) | Joins using a pre-reserved seat from the server. See Match-maker → Reserve Seat For. |
You may disallow the client from creating rooms. See Matchmaker → Restricting the frontend from creating rooms
Example: Creating an invite link with joinById
// Share the room ID with other players
const inviteLink = `https://mygame.com/join?roomId=${room.roomId}`;
// On the receiving end, parse the room ID and join
const params = new URLSearchParams(window.location.search);
const room = await client.joinById(params.get("roomId"));Send and Receive Messages
Once connected to a room, you can send and receive messages in real-time.
Sending Messages
Send messages to the room handler. Messages are encoded with MsgPack and can hold any JSON-serializable data.
//
// sending message with string type
//
room.send("move", { direction: "left"});
//
// sending message with number type
//
room.send(0, { direction: "left"});Backend: See Room → Message Handling for detailed documentation on receiving messages from the client.
Send Raw Bytes
For custom encoding, send raw byte arrays (numbers from 0 to 255).
//
// sending message with number type
//
room.sendBytes(0, [ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]);
//
// sending message with string type
//
room.sendBytes("some-bytes", [ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]);Receiving Messages
Listen for messages sent from the server.
room.onMessage("powerup", (message) => {
console.log("message received from server");
console.log(message);
});Backend: To send a message from the server to a client you’ll need to use either client.send() or room.broadcast()
State Synchronization
The room state is automatically synchronized from the server to all connected clients. The room.state property always contains the latest state.
State Sync Callbacks (Recommended)
Use Callbacks to listen for specific property changes with fine-grained control.
import { Callbacks } from "@colyseus/sdk";
const callbacks = Callbacks.get(room);
callbacks.listen("currentTurn", (currentValue, previousValue) => {
console.log("Turn changed:", previousValue, "->", currentValue);
});
callbacks.onAdd("players", (player, sessionId) => {
console.log("Player joined:", sessionId);
callbacks.listen(player, "hp", (currentHp, previousHp) => {
console.log("Player", sessionId, "hp:", currentHp);
});
});
callbacks.onRemove("players", (player, sessionId) => {
console.log("Player left:", sessionId);
});See the full State Sync Callbacks documentation for all available methods including listen, onAdd, onRemove, and more.
On State Change
The onStateChange event fires whenever the server synchronizes state updates. Use this when you need to know something changed, without tracking individual properties.
room.onStateChange((state) => {
console.log("the room state has been updated:", state);
});Read more about State Synchronization
Connection Lifecycle
Handle the various states of a room connection.
Leaving a Room
Disconnect from the room. Use consented: true (default) for intentional leaves, or false to simulate an unexpected disconnect.
// consented leave
room.leave();
// force unconsented leave
room.leave(false);Backend: Use Room → On Leave to handle client disconnection.
Listening for the leave event:
room.onLeave((code) => {
console.log("client left the room");
});Possible closing codes and their meaning:
1000- Regular socket shutdown- Between
1001and1015- Abnormal socket shutdown - Between
4000and4999- Custom socket close code (See more details)
Automatic Reconnection
The SDK automatically attempts to reconnect when the connection is unexpectedly dropped.
Automatic reconnection only triggers if the room has been connected for at least minUptime milliseconds. This prevents reconnection loops for rooms that fail immediately after joining.
On Drop Event
Triggered when the connection is unexpectedly dropped. The SDK will automatically attempt to reconnect.
room.onDrop((code, reason) => {
console.log("connection dropped, attempting to reconnect...");
console.log("code:", code, "reason:", reason);
});The onDrop event is different from onLeave. While onLeave is triggered when the client intentionally leaves or the connection is permanently closed, onDrop indicates a temporary disconnection where reconnection will be attempted.
Close codes that trigger onDrop:
1005- No status received1006- Abnormal closure1001- Going away4010- May try reconnect
On Reconnect Event
Triggered when the client successfully reconnects after a connection drop. While disconnected, your room.send() calls will be queued and sent to the server when the client reconnects. The maximum number of queued messages is configurable using room.reconnection.maxEnqueuedMessages.
room.onReconnect(() => {
console.log("successfully reconnected to the room!");
});Reconnection Options
You may configure the reconnection behavior using room.reconnection.
Reconnection config options
| Option | Default | Description |
|---|---|---|
maxRetries | 15 | Maximum reconnection attempts |
minDelay | 100 | Minimum delay between attempts (ms) |
maxDelay | 5000 | Maximum delay between attempts (ms) |
minUptime | 5000 | Minimum room uptime before reconnection is allowed (ms) |
delay | 100 | Initial delay between attempts (ms) |
maxEnqueuedMessages | 10 | Maximum buffered messages during reconnection |
backoff | exponential | Delay calculation function |
Status properties (read-only)
| Property | Type | Description |
|---|---|---|
isReconnecting | boolean | Whether currently reconnecting |
retryCount | number | Current reconnection attempt count |
enqueuedMessages | number | Buffered messages |
Customizing reconnection behavior:
const room = await client.joinOrCreate("battle");
// Customize reconnection options
room.reconnection.maxRetries = 10;
room.reconnection.maxDelay = 10000; // 10 seconds max delay
room.reconnection.minUptime = 3000; // Allow reconnection after 3 secondsCustom backoff function:
room.reconnection.backoff = (attempt: number, delay: number) => {
return Math.floor(Math.pow(2, attempt) * delay);
};Manual Reconnection
For more control, you can manually reconnect using a cached reconnection token. Because this method returns a new room instance on the client, you must reattach all event listeners to the room after reconnecting.
- You must store/cache the
room.reconnectionTokenfrom an active room connection. - The server needs to call
.allowReconnection()for that client.
try {
const room = await client.reconnect(cachedReconnectionToken);
console.log("joined successfully", room);
} catch (e) {
console.error("join error", e);
}Error Handling
Listen for errors that occur in the room.
room.onError((code, message) => {
console.log("oops, error occurred:");
console.log(message);
});Removing Listeners
Remove all event listeners from the room.
Latency & Server Selection
Measure network latency and choose optimal servers for your players.
Measuring Latency
Before Joining
Create a temporary connection to measure latency before joining a room.
Parameters
options.pingCount: Number of pings to send (default:1). Returns the average latency when greater than 1.
const latency = await client.getLatency();
console.log("Latency:", latency, "ms");
// With multiple pings for a more accurate average
const avgLatency = await client.getLatency({ pingCount: 5 });
console.log("Average Latency:", avgLatency, "ms");During a Room Connection
Measure round-trip time on an existing connection.
room.ping((latency) => {
console.log("Latency:", latency, "ms");
});If the connection is not open, calling ping() has no effect.
Multi-Region Server Selection
Automatically connect to the server with the lowest latency from a list of endpoints.
Parameters
endpoints: Array of server endpoints (URLs or endpoint settings objects).options: Optional client options to pass to each client instance.latencyOptions.pingCount: Number of pings to send per endpoint (default:1).
import { Client } from "@colyseus/sdk";
// Select the best server from multiple regions
const client = await Client.selectByLatency([
"https://us-east.gameserver.com",
"https://eu-west.gameserver.com",
"https://asia.gameserver.com",
]);
// Now use the client with the lowest latency
const room = await client.joinOrCreate("game");The method logs the latency for each endpoint to the console for debugging purposes. If all endpoints fail to respond, an error is thrown.
HTTP Requests
The client.http utility performs HTTP requests to your server endpoint. The client.auth.token is sent automatically as an Authorization header.
// GET
const response = await client.http.get("/profile");
// POST
const response = await client.http.post("/profile", { body: { name: "Jake" } });
// PUT
const response = await client.http.put("/profile", { body: { name: "Jake" } });
// DELETE
const response = await client.http.delete("/profile");See Server → HTTP Routes for setting up HTTP endpoints on your server, and Authentication → HTTP Middleware for securing them.
Room Reference
Quick reference for room properties.
| Property | Type | Description |
|---|---|---|
state | any | The synchronized room state from the server |
sessionId | string | Unique identifier for the current client connection |
roomId | string | Unique room ID (shareable for direct joins) |
name | string | Name of the room handler (e.g., "battle") |
reconnectionToken | string | Token for manual reconnection |
reconnection | ReconnectionOptions | Automatic reconnection configuration |
Next Steps
- State Sync Callbacks - Listen for state changes on the client
- Room API - Server-side room implementation
- Authentication - Secure your room connections
- Tutorials - Step-by-step game implementations