#SDK reference
Every method on the Arcade global, in one place. The feature
guides explain when to use these; this page is the
signature list, for when you already know what you want and just need the shape
of the call.
Source of truth: packages/sdk/src/index.ts
and types.ts, which carry full TSDoc.
#Loading it
<script src="/sdk/v1/arcade.js"></script> <!-- IIFE, sets window.Arcade -->
import Arcade from "/sdk/v1/arcade.esm.js"; // ESM build, same surface
There is no npm package. In a game's own Vite dev server the script 404s and
Arcade is undefined, so guard with typeof Arcade !== "undefined".
#Failure model
Read this once and the rest of the page needs no caveats.
- Network calls reject with
ArcadeOfflineErrorwhen the API is unreachable. - Calls needing a session return
nullwhen signed out, rather than throwing. - Nothing here is load-bearing for play. Catch, ignore, carry on.
try { await Arcade.leaderboard.submit(score); } catch { /* offline */ }
#Arcade.init(options?)
Call once at startup. Options (ArcadeConfig, all optional):
| Option | Default | Meaning |
|---|---|---|
game |
detected from /games/<slug>/ |
Game slug |
apiBase |
"/api" |
API root; override to point at another origin |
realtimeUrl |
auto-discovered via GET {apiBase}/realtime/info |
WebSocket base |
achievementToasts |
true |
Show the built-in unlock popup |
#Arcade.auth
| Method | Returns |
|---|---|
getUser() |
Promise<ArcadeUser | null>. { email, name, picture? } |
login({ return? }) |
void. Full-page redirect, comes back to return |
logout() |
Promise<void> |
onChange(cb) |
() => void. Fires once immediately, then on tab focus |
getUser() returns null both when signed out and when offline. Treat them the
same. See player accounts.
#Arcade.leaderboard
| Method | Returns |
|---|---|
submit(score, opts?) |
Promise<ScoreEntry>. Integer 0..1e9 |
top(limit = 10, opts?) |
Promise<ScoreEntry[]>. Best per player, limit caps at 100 |
me(opts?) |
Promise<MyScore | null>. { score, at, rank } |
player() |
string | null. The locally remembered display name |
SubmitOptions: player (display name), meta (≤1KB serialized), board (a
named board declared in game.json). Read options take board only. See
leaderboards.
#Arcade.achievements
| Method | Returns |
|---|---|
unlock(id, { toast? }) |
Promise<UnlockResult | null>. null signed out; { new: false } if already owned |
list() |
Promise<GameAchievements>. Catalog + unlock counts + your progress |
unlockers(id, limit = 50) |
Promise<Unlocker[]>. Earliest earner first |
mine() |
Promise<MyAchievements | null>. GS across every game |
watch() |
void. Idempotent; polls every 20s and on focus for server-granted unlocks |
onUnlock(cb) |
() => void. Fires even when toast: false |
preview(a, gameName?) |
void. Render the popup with arbitrary content (styling/debug) |
setMuted(b) / muted() |
void / boolean. The chime only; popup still shows |
Ids are bare in your code ("first-flight"); the platform qualifies them as
<slug>#<id> in storage, so cross-game unlocks are structurally impossible. See
achievements.
#Arcade.realtime
join(roomId) → Promise<RealtimeRoom>. Room ids are scoped to your game slug,
so match:abc in two games never collide. Auto-reconnects with capped backoff.
| Member | Meaning |
|---|---|
id, connected, members |
Readonly state |
send(data) |
Broadcast to everyone else, not echoed back |
match(data) |
Server-authoritative action → your backend's realtimeMatch |
on(event, cb) |
"message" | "presence" | "open" | "close"; returns unsubscribe |
close() |
Leave |
See multiplayer.
#Arcade.invites, Arcade.players, Arcade.notifications
| Method | Returns |
|---|---|
invites.send({ to, kind, payload? }) |
Promise<Invite> |
invites.inbox() |
Promise<Invite[]>. Cross-game, keyed to the player |
invites.accept(id) / decline(id) |
Promise<Invite> |
invites.onChange(cb) |
() => void. Live inbox, drives a badge |
players.search(query) |
Promise<RoomMember[]>. Typeahead |
notifications.mount(el, { onAccept? }) |
() => void. Self-contained bell + badge + dropdown |
notifications.mount injects its own styles. Accepting navigates to
invite.payload.url unless onAccept is supplied.
#Arcade.nav
The arcade top bar is on every page, games included. A game gets an optional drawer under it, and a guard against being navigated away mid-play.
| Method | Returns |
|---|---|
open({ name?, icon?, actions?, stats? }) |
void. Opens/updates the drawer; actions caps at 3, stats at 4 |
stats(items) |
void. Values only, keeping the name, icon and actions |
actions(items) |
void. Buttons only, keeping the name, icon and stats |
clear() |
void. Closes the drawer |
guard(on) |
void. string = on with that wording, true = on generic, false = off |
NavStat: { icon?, value, label? } — icon is an emoji or an image URL
(anything starting / or http), value shows bold, label is the tooltip and
accessible name rather than visible text.
NavAction: { label, icon?, onClick?, disabled?, primary?, menu?, panel? } — a
button in the actions drawer, a second drawer under the account menu (Back, Save,
Cancel, Invite). menu makes it a dropdown of NavMenuItem rows
({ label, icon?, onClick, disabled? }); panel(el) makes it a dropdown the game
fills itself, returning an optional cleanup that runs on close. open() replaces
the action list, so a screen that wants buttons asks after it renders.
The bar sits at z-index: 500. A game's own layers belong below that; a
full-screen overlay above it hides the only way back to the arcade.
On a game page the arcade also sets padding-top: var(--arcade-nav-h) on
<body>, so page flow already clears the bar. Out-of-flow elements and
viewport-sized shells still need to account for it themselves — see
the guide.
guard covers link clicks that leave the game (our dialog, your wording) and tab
close/reload (the browser's dialog, its wording). Opening the drawer republishes
--arcade-nav-h on :root with the real total height, which is what a game
should use to keep its own chrome clear of the bar.
Full guide: the arcade nav bar.
#Arcade.voice
attach(room) → VoiceSession. Cheap; grabs no hardware until join().
| Method | Notes |
|---|---|
join() |
Promise<void>. Acquires the mic, needs a user gesture |
leave() |
Full teardown, safe when idle |
setMuted(b) |
Mutes locally and tells the peer |
on("state", cb) |
Fires immediately, then on change; returns unsubscribe |
mountControls(el) |
Stock mic button + speaking dot; returns unmount |
VoiceState: { status: "idle"|"connecting"|"connected"|"failed", muted, peerPresent, peerMuted, speaking, peerSpeaking }. 1v1 only, STUN-only. See
voice chat.
#Exported types
Achievement · ArcadeConfig · ArcadeUser · ArcadeVoice · BoardOptions ·
GameAchievement · GameAchievements · Invite · MyAchievements · MyScore ·
RealtimeRoom · RoomMember · ScoreEntry · SubmitOptions · Unlocker ·
UnlockResult · VoiceSession · VoiceState
See also: architecture · local development