Client SDK

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.

client.ts
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.

The most common way to connect. Joins an existing room if available, or creates a new one.

client.ts
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.

MethodSignatureDescription
createclient.create(roomName, options)Always creates a new room, even if others exist.
joinclient.join(roomName, options)Joins an existing room. Fails if none available. Locked/private rooms are ignored.
joinByIdclient.joinById(roomId, options)Joins a specific room by its unique ID. Private rooms can be joined by ID. Useful for invite links.
consumeSeatReservationclient.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

client.ts
// 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.

client.ts
//
// 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.

client.js
//
// 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.

client.ts
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.

client.ts
//
// 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:

client.ts
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.

client.ts
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.

Use Callbacks to listen for specific property changes with fine-grained control.

client.ts
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.
client.ts
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.

client.ts
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).
client.ts
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.

PropertyTypeDescription
stateanyThe synchronized room state from the server
sessionIdstringUnique identifier for the current client connection
roomIdstringUnique room ID (shareable for direct joins)
namestringName of the room type (e.g., "battle")
reconnectionTokenstringToken for manual reconnection
reconnectionReconnectionOptionsAutomatic reconnection configuration
clockRoomClockServer time & latency estimates (serverNow(), rtt()). See room.clock

Next Steps