SDK
SDK reference
Version 0.2.0. All methods are on window.OpenArcade (also available as window.SmazGames). Install it first: see Install the SDK.
Unless stated otherwise, methods return a promise. Methods that return nothing never throw; failures are logged in debug mode.
Quick list
| Area | Members |
|---|---|
| Start-up | init, environment, version, setDebug |
| Lifecycle | loadingStart, loadingProgress, loadingStop, gameplayStart, gameplayStop, happyMoment, onHost |
| Saves | load, save, importGuest, data.getItem, data.setItem, data.removeItem, data.clear, data.keys |
| Player | getUser, promptSignIn, getPlayerToken |
| Ads | showRewardedAd, showMidgameAd, requestAd, isAdBlocked, requestBanner, clearBanner |
| Settings | getSettings, onSettingsChange, getSystemInfo, isMobile |
| Links and invites | getURLParam, getInviteLink, shareInvite, showInviteButton, hideInviteButton, openExternalLink |
| Multiplayer | multiplayer.createRoomCode, multiplayer.getRoomCode, multiplayer.inviteLink, multiplayer.share, multiplayer.updateRoom, multiplayer.leaveRoom |
| Errors and events | captureError, setGameContext, track |
Start-up
init(options)
Connects to the site. Call it once, as early as possible. Later calls return the same promise. Methods that need the site's answer, such as saves, getUser and invites, call it for you and wait for it.
| Option | Type | Default | Meaning |
|---|---|---|---|
debug | boolean | false | Log every call to the console |
captureErrors | boolean | false | Report uncaught errors and unhandled promise rejections to your error log |
Resolves to:
{
environment: 'smaz', // 'local' outside Smaz Games
user: null, // or a player object, see getUser()
cloudSupported: false, // true when saves go to the player's account
guestSaveAvailable: false, // a guest save exists in the 'progress' slot
muted: false,
paused: false,
adsEnabled: false, // true when a rewarded ad can be requested
adTypes: [], // ['rewarded'] when available
settings: { /* see getSettings() */ },
params: { room: 'K7Q2PX' }, // allowed query parameters from the game page
game: { id, slug, buildId, version }
}Apply muted and paused straight away. onHost reports only later changes.
If init() rejects, the game frame could not reach the site. Show the game anyway; saving will fail until the player reloads.
const env = await OpenArcade.init({ captureErrors: true });environment
'smaz' inside the Smaz Games player, 'local' anywhere else. Not a promise.
version
The SDK version string, '0.2.0'.
setDebug(on)
Turns console logging on or off. The choice is remembered in this browser where browser storage is available. Returns nothing.
Lifecycle
These calls tell the site what the game is doing. They return nothing and never throw.
loadingStart()
Call when your loading screen appears. The site shows a progress bar along the bottom of the player.
loadingProgress(fraction)
fraction is a number from 0 to 1. Call as often as you like; values are clamped.
loadingStop()
Call when loading has finished and the first screen is ready. The site hides the progress bar and records the load time.
gameplayStart()
Call when the player is actually playing: after loading, after a menu closes, after a pause or ad ends. Play time is counted only between gameplayStart and gameplayStop, while the game is visible, not paused and in a supported orientation. Returns a promise resolving to { ok: true }.
gameplayStop()
Call for menus, pauses, level-end screens, dialogs and before ads. Returns a promise resolving to { ok: true }.
happyMoment()
Call at a moment of success, such as finishing a level or beating a record. The site records it as an event. Use it at most a few times per session.
onHost(listener)
Calls listener(message) when the site changes pause, sound or visibility. Returns a function that removes the listener. Not a promise.
const stop = OpenArcade.onHost(({ paused, muted, visible, ad }) => {
// paused: the site's pause button, the tab is hidden, the phone needs rotating, or an ad is playing
// muted: the site's sound button, or an ad is playing
// ad: true while an ad is showing, false when it ends
});Combine the site's pause with your own pause menu: resume only when both allow it. Keep listeners short.
Saves
See Saves and cloud data for where progress goes and how conflicts work.
load(slot)
Loads a save slot. slot defaults to 'progress'. Resolves to { data, revision, schemaVersion, updatedAt }, or { data: null, revision: 0, schemaVersion: 1 } for an empty slot. The SDK remembers the revision for the next save.
save(slot, data, schemaVersion)
Saves data (plain JSON, up to 1MB) to a slot you have loaded. schemaVersion defaults to 1. Saves run one at a time in the order you call them. Resolves to { revision, updatedAt }.
Rejects with status 409 if the slot changed since you loaded it (another tab or device saved), 413 if the data is over 1MB, and 400 for an invalid slot or schema version.
const save = await OpenArcade.load('progress');
const state = save.data ?? { level: 1, coins: 0 };
state.level++;
await OpenArcade.save('progress', state, 1);importGuest(slot)
Moves progress the player made as a guest into their empty account slot, and removes the guest copy. Offer it only when the player asks. Rejects if the account slot already has progress. Resolves like load.
data.getItem(key)
Key-value storage on top of the data save slot, with a localStorage-style interface. Unlike localStorage, it follows a signed-in player to other devices. Resolves to the stored string, or null. Keys are 1 to 100 characters.
data.setItem(key, value)
Stores String(value). Changes made within 50 milliseconds are sent together. Resolves when saved. Use JSON.stringify for objects. Rejects with 400 for an invalid key and 413 if the whole data slot would be over 1MB.
data.removeItem(key)
Removes one key. Resolves when saved.
data.clear()
Removes every key in the data slot. Cannot be undone.
data.keys()
Resolves to an array of stored keys.
const best = Number(await OpenArcade.data.getItem('best') ?? 0);
if (score > best) await OpenArcade.data.setItem('best', score);Player
See Player accounts and tokens.
getUser()
Resolves to null for a guest, or:
{
playerId: 'Zq3…', // stable, unique to your game; use this to identify the player
username: 'pixel_fox', // public handle, can change
name: 'Pixel Fox', // display name, can change
avatar: 'rocket', // preset avatar name, or 'custom' for an uploaded picture
minor: false // true unless the account belongs to an adult
}Games never receive the player's email address, sign-in details, session, exact age, friends or saves of other games.
promptSignIn()
Opens the site's sign-in dialog. Resolves to { status: 'shown' }, or { status: 'signed-in' } if the player is already signed in. After the player signs in, the game frame reloads and init() reports the new player, so save state before calling it. Call only from a button the player pressed. Limited to 4 calls per minute.
getPlayerToken()
Resolves to { token, expiresAt, player }, where token is a signed JWT your own server can verify. Valid for 10 minutes; request a new one each time you need it. Rejects with 401 for guests. Each player can get up to 20 tokens a minute. See Verifying tokens.
Ads
See Ads. Every ad call resolves, whether or not an ad played. Grant a reward only when rewardGranted is true.
showRewardedAd()
Requests a rewarded ad. Call only after the player chose to watch one. Resolves to { status, rewardGranted, reason? }:
status | rewardGranted | Meaning |
|---|---|---|
'completed' | true | The ad network confirmed the reward |
'dismissed' | false | The player closed the ad early |
'unavailable' | false | No ad was shown. See reason |
reason is 'disabled' (ads are off for this game or site), 'busy' (another ad is showing), 'frequency' (too soon after the last ad), 'no-fill' (no ad, blocked or timed out), 'unsupported' (unknown ad type) or 'local' (local test mode).
showMidgameAd()
Requests an ad at a natural break, such as between levels. Midgame ads are not enabled on Smaz Games at present, so this resolves to { status: 'unavailable', rewardGranted: false, reason: 'disabled' } straight away, or with reason 'frequency' if the last midgame request was less than 3 minutes ago. Calling it at your breaks now means nothing needs to change if they are enabled later. At most one will be shown every 3 minutes.
requestAd(type)
The original form. requestAd('rewarded') is showRewardedAd(); requestAd('midgame') and requestAd('interstitial') are showMidgameAd().
isAdBlocked()
Resolves to true if ads are live and the site's ad script could not load, usually because of an ad blocker. Resolves to false when ads are off. Never penalise a player for blocking ads.
requestBanner(options)
Banners on Smaz Games are placed by the site around the player, never inside a game. This call always resolves to { status: 'unavailable', reason: 'host-rendered' }. It exists so games built for banner calls work without changes.
clearBanner(id)
Resolves to { status: 'unavailable', reason: 'host-rendered' }.
Settings
See Settings and accessibility.
getSettings()
Returns the current settings straight away (not a promise):
{
muted: false, // site sound button
reducedMotion: false, // the player's system asks for less motion
locale: 'en-GB', // browser locale
language: 'en',
disableChat: true, // true unless the player is a signed-in, verified adult
isMobile: false,
device: 'desktop', // 'desktop', 'mobile' or 'tablet'
orientation: 'any' // the orientation declared for this game
}onSettingsChange(listener)
Calls listener(settings) with the full settings object whenever the site changes one. Returns an unsubscribe function.
getSystemInfo()
Returns information about the player's device (not a promise):
{ device: 'mobile', isMobile: true, touch: true, os: 'ios', browser: 'safari',
screen: { width: 390, height: 844 }, pixelRatio: 3 }os is one of windows, macos, ios, android, chromeos, linux, other. browser is one of chrome, edge, firefox, safari, other. Use it for layout and controls, not to block browsers.
isMobile()
true on phones and tablets. Not a promise.
Links and invites
getURLParam(name)
Returns the value of a query parameter from the game page address, or null. Not a promise; call after init() resolves. Only parameters with names of 1 to 24 letters, numbers or _ starting with a letter, and values of 1 to 64 letters, numbers, _ or -, are passed, up to 10 of them. play, channel, portalOrigin and names starting with smaz are reserved.
getInviteLink(params)
Resolves to the address of your game page with params added, for example https://smazgames.com/game/your-game-x7k?play=1&room=K7Q2PX. Opening it starts the game straight away, and getURLParam('room') returns 'K7Q2PX'. Up to 5 parameters, with the same name and value rules as getURLParam. Rejects with 400 if a name or value breaks the rules, a name is reserved, or there are more than 5.
shareInvite(params, options)
Copies the invite link to the player's clipboard and shows a confirmation. Resolves to { status: 'copied', url }, or { status: 'unavailable', url } if the clipboard could not be used; then show the url in your game. Call from a button the player pressed. options.text (up to 140 characters) is reserved for share sheets.
showInviteButton(params)
Shows an Invite button in the player toolbar that copies the invite link. Resolves to { ok: true, url }. Call it when a joinable room exists.
hideInviteButton()
Removes the Invite button. The button is also removed when the game reloads.
openExternalLink(url)
Asks the player whether to open url in a new tab. The address must use https and its origin must be one of your game's declared outside addresses. Resolves to { status: 'opened' } or { status: 'cancelled' }. Rejects with 403 for undeclared addresses. Limited to 5 calls per minute.
Multiplayer helpers
multiplayer.createRoomCode(length)
Returns a random code (not a promise) of length characters (default 6, from 4 to 12) using capital letters and digits without look-alikes (0, O, 1, I). Your server decides whether a code is valid.
multiplayer.getRoomCode()
Returns the room parameter from an invite link, or null. Not a promise.
multiplayer.inviteLink(room)
Same as getInviteLink({ room }).
multiplayer.share(room, options)
Same as shareInvite({ room }, options).
multiplayer.updateRoom({ room, joinable })
Shows the Invite button for room when joinable is true (the default), and hides it otherwise.
multiplayer.leaveRoom()
Hides the Invite button.
await OpenArcade.init();
let room = OpenArcade.multiplayer.getRoomCode();
if (!room) room = OpenArcade.multiplayer.createRoomCode();
await connectToMyServer(room, (await OpenArcade.getPlayerToken()).token);
OpenArcade.multiplayer.updateRoom({ room, joinable: true });Errors and events
captureError(error, context)
Sends an error to the error log on your dashboard. error can be an Error or a string. Resolves to { ok, sent } and never rejects: sent is false when the report could not be delivered or was over the limit. Limited to 30 reports per minute.
setGameContext(context)
Attaches a small JSON object (up to about 1KB once serialised), such as { level: 4, mode: 'versus' }, to later error reports. Pass null to clear it. Do not include personal data. Returns nothing.
track(event, params)
Sends a custom analytics event. event is 1 to 40 lowercase letters, numbers and _, starting with a letter. params is up to 10 values: numbers, booleans or strings up to 100 characters. Resolves to { ok: true, sent }. Rejects with 400 for an invalid name. Limited to 60 events per minute. See Analytics events.
OpenArcade.track('level_complete', { level: 3, seconds: 74, stars: 2 });Errors from the SDK
Failures are ordinary Error objects. Errors from the site have a numeric status:
| Status | Meaning |
|---|---|
400 | Invalid input, such as a bad slot name or event name |
401 | No player is signed in |
403 | Not allowed, such as opening an undeclared link |
404 | The game is not published |
409 | Save conflict: newer progress exists |
413 | Save data over 1MB |
429 | Too many requests. Wait a minute |
A request with no answer after 15 seconds (2 minutes for ads and openExternalLink) rejects without a status. A save that timed out may still have succeeded, so reload the slot before trying again. Do not rely on the wording of error messages.
Rate limits
Per game frame, per minute. Calls over the limit reject with 429; lifecycle calls over the limit are dropped. The server also allows each player 20 player tokens a minute across all frames.
| Method | Limit |
|---|---|
track | 60 |
captureError | 30 |
getPlayerToken | 20 |
getUser | 30 |
getInviteLink, showInviteButton, hideInviteButton | 30 |
shareInvite | 10 |
happyMoment | 10 |
isAdBlocked | 10 |
requestBanner, clearBanner | 30 |
| ad requests | 20 |
openExternalLink | 5 |
promptSignIn | 4 |
loadingStart, loadingProgress, loadingStop (together) | 300 |
| Every other call, including saves | 600 |
Message protocol
The SDK talks to the site with postMessage. Messages are bound to the frame that the site created and a random channel for that frame, and the site answers only the frame's own window. You do not need to use the protocol directly, and it may change between versions; use the SDK file.