npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

Readme

@gamerelay/sdk

CI npm gzip size types license

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 (the v0 line), 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/sdk

Or 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 host

Open 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