Everything a browser game needs is available without plugins, proxies or a server of your own — and the constraints that do exist are honest ones, documented here rather than discovered at launch.
A web page can call the complete REST surface: player registration and sessions, room create/join/leave, matchmaking, leaderboards, friends and invites, per-player JSON storage, server-side moderation and time sync. Every /api/*.php endpoint answers CORS preflights with Access-Control-Allow-Origin: * and whitelists the custom auth headers, so fetch() works directly — no CORS proxy, no JSONP, no extensions.
For live traffic the realtime relay speaks WebSocket, which every browser has supported for over a decade. The handshake accepts the realtime token as a query parameter precisely because browsers cannot set headers on a WebSocket upgrade — this is a deliberate server-side allowance for web clients, not a workaround.
const res = await fetch('https://api.michitai.com/api/game_players.php/heartbeat', {
method: 'POST',
headers: {
'X-Api-Token': API_TOKEN,
'X-Game-Player-Token': playerToken
}
});
const body = await res.json(); // { success: true, ... }
Browsers cannot send raw UDP datagrams. The MUR1 fast lane that native clients use for movement-rate traffic (positions, inputs, analog state) is unavailable in a web page — there is no browser API for it. What happens instead: the server already delivers UDP-bound room traffic over WebSocket to peers that never bound a datagram address, and the JavaScript SDK's sendUnreliable() falls back to the WebSocket lane automatically.
Practically: a browser game gets one reliable ordered channel. For twitch gameplay at high update rates, batch state into fewer, smaller messages, or gate send rate. Turn-based, co-op, card, trivia, party and most .io-style games are comfortably inside what the WS lane handles — that's the design center for browser deployment.
The alternative browser datagram transport, WebTransport over HTTP/3, is evaluated on its own page — it requires server-side HTTP/3 infrastructure the current relay doesn't run, so it isn't offered today.
The JavaScript SDK is the same package browsers and Node.js share — plain ESM, zero dependencies, no build step. Import it from a bundler, an npm install, or copy src/ into a static project.
import { MultiplayerClient } from 'michitai-multiplayer';
const mp = new MultiplayerClient('API_TOKEN'); // public token only
const reg = await mp.players.register('WebPlayer');
const playerToken = reg.private_key; // persist via localStorage
const room = await mp.rooms.create(playerToken, 'Web Lobby', {
maxPlayers: 8, realtime: true
});
const tok = await mp.realtime.get(playerToken);
const rt = mp.createRealtime({
onReceive: (cmd, data, sender) => applyMessage(cmd, data, sender),
});
await rt.connect(tok.token, tok.realtime_server); // wss://, query-token auth
rt.send('all', 'move', { x: 10, y: 4 });
Realtime tokens are short-lived (a 5-minute connect window) and single-connection — mint one per session through the SDK rather than hardcoding anything.
Unity's WebGL target compiles to wasm and restricts networking to what the browser offers: UnityWebRequest works (it maps to fetch), and .NET UdpClient/raw sockets do not — there is no socket API in a web page. For Unity WebGL, use the REST endpoints for lobby/room management (the Unity SDK's ?format=unity conventions apply), and the realtime channel through the browser's WebSocket — reachable from Unity via a JavaScript interop plugin or the JavaScript SDK alongside your build.
Other engines with HTML5 export (Godot 4 web builds, Construct, Defold, PlayCanvas) are in the same position: HTTP requests work, WebSocket works, UDP doesn't. Any engine that can make an HTTPS request can use this API — see Any Engine.
Anything shipped to a browser is visible to its user. Ship only the public API token and per-player tokens — both are scoped to your game and grant no admin power. The private token (X-Api-Private-Token) must live exclusively on infrastructure you control; embedding it in a web bundle hands strangers ban/unban and game-data write access.
Player tokens are per-game and rotatable via /api/game_players.php/rotate; treat them like session cookies — store in localStorage or your own session layer, never in URLs or third-party analytics events.
fetch/wss from secure pages, and the realtime relay is TLS-only.localStorage) so returning players keep their account.sendUnreliable semantics for ephemeral state (it degrades to WS in-browser).