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 Matchmaker → 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. 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 a Uint8Array of raw bytes.
//
// sending message with number type
//
room.sendBytes(0, new Uint8Array([ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]));
//
// sending message with string type
//
room.sendBytes("some-bytes", new Uint8Array([ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]));The payload must be a Uint8Array. A plain number[] array silently sends an empty message, since the SDK reads .byteLength from the payload.
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, use client.send() or room.broadcast().
Request/Response
Send a message and await the server’s reply. The server answers by returning a value from its matching message handler.
//
// Promise form: room.request(type, payload)
//
const profile = await room.request("get-profile", { userId: 42 });
//
// Override the default timeout (10s) per request
//
const result = await room.request("slow-task", { id }, { timeout: 30_000 });
//
// Callback form: pass a 3rd argument to room.send()
//
room.send("get-profile", { userId: 42 }, (profile, error) => {
if (error) { return console.error(error); }
console.log(profile);
});The promise rejects (or the callback receives an error) when the handler throws or no handler is registered for that type. Rejection also happens when the connection closes first, or when no reply arrives within the timeout. The default timeout is Room.defaultRequestTimeout (10000 ms), overridable per request via the timeout option.
The server may also deliberately reject a request via ctx.reject(reason): a business-logic “no”, distinct from an error. The rejection arrives as an Error with name: "rejected", and the server’s raw reason on its .reason property:
try {
await room.request("join-team", { team: "red" });
} catch (e: any) {
if (e.name === "rejected") {
console.log(e.reason); // => { code: "TEAM_FULL", team: "red" }
}
}Pass mode: "unreliable" in the options to send the request over the unreliable channel, when the transport has one. A genuine packet drop is then surfaced by the timeout.
Request/response is currently available in the JavaScript/TypeScript SDK.
Sending Input (Netcode)
For prediction-ready rooms (rooms that call defineInput() on the server), room.input() returns a typed input channel: stage fields on input.data, then input.send() once per fixed step.
const input = room.input(); // input schema arrives from the server's defineInput()
input.data.moveX = 1;
input.data.jump = true;
input.send();Inputs are buffered and acknowledged, and power client-side prediction and lag compensation. See room.input(options) for the full reference: reliability modes, redundancy, and the InputHandle surface.
The netcode APIs (room.input(), room.clock, Predict) are available in Colyseus 0.18+ across the official SDKs. See Client Prediction.
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
Leaving a room, automatic and manual reconnection, error handling, and removing listeners. See Connection Lifecycle & Reconnection for the full reference.
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 type (e.g., "battle") |
reconnectionToken | string | Token for manual reconnection |
reconnection | ReconnectionOptions | Automatic reconnection configuration |
clock | RoomClock | Server time & latency estimates (serverNow(), rtt()). See room.clock |
Next Steps
- State Sync Callbacks - Listen for state changes on the client
- Netcode - Predict local input and reconcile to server state
- Room API - Server-side room implementation
- Authentication - Secure your room connections
- Tutorials - Step-by-step game implementations