@gamerelay/sdk
v0.1.0-alpha.12
Published
Multiplayer for browser games without a server: rooms, entity sync with smoothing, host migration, chat and leaderboards. Zero dependencies.
Maintainers
Readme
@gamerelay/sdk
Multiplayer for browser games, without writing a server. You declare what exists in your game and who owns it; the SDK sends it, smooths it for everyone else, gives it to players who join late, and hands the host's share to another player when the host leaves. Rooms, quick match, invite links, parties, chat, saved player data and leaderboards come with it. Zero dependencies, fully typed, about 32 KB gzipped.
Docs: gamerelay.io/docs · Guide for AI tools:
gamerelay.io/llms.txt (also in this package as llms.txt) ·
Demo: gamerelay.io/demo
Alpha. Within
0.x(thev0line), nothing is removed or renamed without a deprecation first: the old name keeps working, and warns, for at least 30 days. See Versions.
Install
npm i @gamerelay/sdkOr without a build step:
<script src="https://gamerelay.io/sdk/v0/gamerelay.js"></script>
<!-- exposes window.GameRelay -->Get a public key (gr_pub_…) by signing in at gamerelay.io and
creating an instance for your game. It's safe to ship in client code.
Quick start
import { GameRelay } from '@gamerelay/sdk';
const relay = await GameRelay.connect({ publicKey: 'gr_pub_…', playerName: 'ada' });
// An invite link's room, else anyone's (or createRoom() / joinRoom('K7QM')).
const room = (await relay.joinInvite().catch(() => null)) ?? (await relay.quickMatch({ maxPlayers: 4 }));
history.replaceState(null, '', room.inviteUrl()); // the address bar invites, and a reload comes back
// Anything that moves is an entity. You write yours; everyone sees everyone's, smoothed.
const ships = room.define('ship', { x: 'number', y: 'number' });
const me = ships.spawn({ x: 100, y: 100 });
relay.tick(60, (dt) => {
me.x += input.x * 200 * dt; // just write fields: the SDK sends the changes
me.y += input.y * 200 * dt;
});
(function draw() {
for (const s of ships.all()) drawShip(s.x, s.y, s.mine);
requestAnimationFrame(draw);
})();
room.emit('wave', { x: me.x, y: me.y }); // a moment everyone sees, you included
room.on('wave', ({ x, y }) => ripple(x, y));
if (room.isHost) room.setState({ round: 1 }); // facts: one shared document, written by the hostOpen your game in two tabs to test: each tab is its own player.
One player's browser is the host. Entities spawned with { owner: 'host' } (a ball,
enemies, pickups), room.state and timers belong to the host role, so when the host leaves,
the next one carries on from where it was. Most games fit one of three shapes: players own
avatars (shooters, racers), host simulates (ball games, anything that must be fair), or
mixed (players move themselves, the host owns the world). llms.txt has a complete,
single-file starter for each.
In TypeScript, room.define types each entity from its fields, so a misspelled or undeclared
field doesn't compile.
Invite links
To play with friends, put the room in the page URL and send the link:
// Came in through an invite link? Join that room; otherwise make a new one (private: not quick
// match). updateUrl: the address bar is now the invite link.
const room = await relay.joinOrCreate({ maxPlayers: 4, private: true, updateUrl: true });A full or locked room still rejects (room_full, locked): tell the player.
room.shareInvite() opens the share sheet on phones and copies the link elsewhere. It shares
the room's short link, https://play.gamerelay.io/<game>/<link> once the game has a slug and a
play URL (set in the dashboard), which previews with the room's name and the game's cover image; joinInvite() joins
it (?join=). createRoom({ linkOnly: true }) makes a room that only its link gets into.
Versions
https://gamerelay.io/sdk/v0/gamerelay.js (and .mjs) is the v0 line and updates in place;
/sdk/gamerelay.js is the same line, forever. To pin one exact version, load it from npm through
jsDelivr: https://cdn.jsdelivr.net/npm/@gamerelay/[email protected]/gamerelay.js. Until the SDK
reaches 1.0, a pinned alpha may be asked to update: new server features can need the newest alpha,
and old alphas stop being accepted sooner than they will after 1.0.
GameRelay.version is the running version. The server may print a notice in the console
([gamerelay] …) when a version gets old; one it no longer accepts makes connect() reject with
upgrade_required. Every rejection is a GameRelayError with a code. What changed in each
release, and what to change in a game, is in CHANGELOG.md.
Everything else
Events, requests to the host, claims, inputs, timers, teams, lobbies and parties, host controls
(lock, resize, name and kick), chat,
leaderboards, signed-in players, webhooks, moderation, allowed origins, testing on a bad network
and limits are in the docs. While developing, pass debug: true to
connect() for an overlay; the SDK also warns in the console, once each, about common mistakes
and names the fix.
Two things worth knowing before you build a lobby: a kick's ban is per player id, and an
anonymous player in a private window gets a new one, so lock the room
(room.setAccess({ locked: true })) to keep strangers out. A room's name and meta come from a
player (the host): render them as text, never as HTML.
License
MIT
