Docs / Quick start - JavaScript / TypeScript

Quick start - JavaScript / TypeScript

Connect a browser or Node game to a running Asobi server in about five minutes. No server yet? Run the server quickstart first - it gives you a backend on localhost:8084 with a default match mode, which is what this page connects to.

1. Install

The package is @widgrensit/asobi. It is not on npm yet, so install it from GitHub (the prepare script builds it on install):

npm install github:widgrensit/asobi-js

It is ESM-only and targets Node 22+ (it uses the global WebSocket and fetch). On Node 18/20, install ws and assign it to globalThis.WebSocket before importing. Browsers work as-is with any bundler (Vite, esbuild, Webpack).

2. Authenticate and open the socket

Create the client, get a guest session, then open the realtime socket - it reuses the stored access token automatically. You generate and persist the device_secret yourself (>= 32 random bytes, base64); the same device_id + secret resumes the same guest on the next launch.

import { Asobi } from "@widgrensit/asobi";

const asobi = new Asobi({ baseUrl: "http://localhost:8084" });

const session = await asobi.auth.guest({
  device_id: deviceId,       // stable per-device id
  device_secret: deviceSecret // your >= 32-byte base64 secret
});
// session.player_id, session.access_token, session.created

const ws = asobi.websocket();   // derives ws://localhost:8084/ws + token
await ws.connect();             // sends session.connect and authenticates

3. Join a match and exchange state

There is no join(mode) helper - you queue the matchmaker and the server pushes the match once it forms. Subscribe before you queue, then send input with the fire-and-forget sendFire:

ws.on("match.matched", (p) => console.log("in match", p.match_id));
ws.on("match.state", (s) => render(s.players)); // players keyed by player_id

ws.sendFire("matchmaker.add", { mode: "default" });

// later, each frame or on input:
ws.sendFire("match.input", { data: { move_x: 1, move_y: 0 } });

mode must be a mode your server actually defines. The server quickstart ships default; a demo backend may serve demo instead.

Core API

  • new Asobi({ baseUrl, accessToken?, onTokens? }) - root SDK (REST + realtime).
  • asobi.auth.guest({ device_id, device_secret }) - guest auth; also register, login, upgradeGuest.
  • asobi.websocket() then ws.connect() - open and authenticate the realtime socket.
  • ws.on(event, cb) - subscribe to pushed events (returns an unsubscribe fn; "*" matches all).
  • ws.sendFire(type, payload) - fire-and-forget (input); ws.send(type, payload) - RPC that awaits a reply (10s timeout).

Gotchas

  • Tokens live in memory only. Pass onTokens to persist them, and store your guest device_secret yourself - otherwise every launch creates a new account.
  • Handle auth expiry. On invalid_token / idle timeout the socket emits auth_expired and stops reconnecting - re-auth and reconnect.
  • Use wss in production. A browser on an https page cannot open ws:// - serve the backend over TLS so the URL becomes wss://.

What's next