#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 ArcadeOfflineError when the API is unreachable.
  • Calls needing a session return null when 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

View docs/technical/sdk-reference.md on GitHub ↗