Skip to content
GEMSARCADIA
▸ MANUAL CONTENTS(SDK reference)
GEMS ARCADIA · COIN-OP SERVICE MANUAL · FORM GA-DEV-1SEC. 2.1

SDK reference

The SDK is a 2.5 KB script that exposes one global, GemsArcadia. Load it from the arcade:

HTML
<script src="https://gemsarcadia.com/sdk/v1.js"></script>

Or install it with a bundler and import the same functions (ESM, with types):

JS
import { init, insertCoin, on } from "@gems-arcadia/sdk";

await init({ host: "https://gemsarcadia.com" });

This page covers SDK 1.1.0 (see the changelog; GemsArcadia.version tells you which version is loaded). Every method returns a promise. Only init() can reject; the others always resolve with { ok: true, ... } or { ok: false, reason }.

init(options?)#

Connects to the arcade. Call it once on load (calling it again returns the same answer).

OptionTypeMeaning
hoststringArcade origin. Defaults to the origin the SDK script was loaded from.
debugbooleanLog every bridge message to the console.

Resolves with:

FieldTypeMeaning
player.refstringStable pseudonymous id (pr_...), the same across all of your games.
player.displayNamestringA name to show in your game.
coinsPerCreditnumberCoins one credit costs (set in the portal, 1–5).
balancenumberThe player's coins.
testModebooleantrue in previews and mock mode: insertions are free.

Rejects if the arcade doesn't answer within 10 seconds.

game.js · init
export const ready = GemsArcadia.init().then((session) => {
  const { player, coinsPerCredit, balance, testMode } = session;
  ui.message.textContent =
    `Hi ${player.displayName}! ${balance} coins, ` +
    `${coinsPerCredit} per credit${testMode ? " (test mode)" : ""}.`;

  // Coins inserted with the cabinet's own INSERT COIN button.
  GemsArcadia.on("coinInserted", ({ insertionId }) => grantCredit(insertionId));
  // The player switched tabs: stop the clock.
  GemsArcadia.on("pause", () => (paused = true));
  GemsArcadia.on("resume", () => (paused = false));

  GemsArcadia.ready(); // loaded and ready to take coins
  return session;
});

insertCoin()#

Asks the arcade to take coinsPerCredit coins from the player for one credit. Resolves { ok: true, insertionId, balance } when the coin went in. Never rejects.

game.js · insert coin
const REASONS = {
  INSUFFICIENT_COINS: "Out of coins! Top up at the Token Booth.",
  NOT_SIGNED_IN: "Log in to play.",
  EMAIL_NOT_VERIFIED: "Verify your email to play.",
  SESSION_EXPIRED: "Session timed out. Reload the game.",
  RATE_LIMITED: "Easy there! One coin at a time.",
  BUSY: "A coin is already dropping...",
};

/** Coins inserted from inside your game. insertCoin() never rejects. */
export async function insertCoin() {
  ui.coinButton.disabled = true;
  try {
    const result = await GemsArcadia.insertCoin();
    if (result.ok) grantCredit(result.insertionId);
    else ui.message.textContent = REASONS[result.reason] ?? "The coin slot jammed. Try again.";
    return result;
  } finally {
    ui.coinButton.disabled = false;
  }
}

ui.coinButton.addEventListener("click", insertCoin);

Failure reasons:

ReasonWhat happenedTell the player
INSUFFICIENT_COINSThe wallet can't cover a credit. Nothing was charged.Top up at the Token Booth
RATE_LIMITEDMore than 1 coin per 500 ms (or 60 per minute).Wait a moment
BUSYA coin is already on its way in.Nothing
SESSION_EXPIREDThe play session ended or idled for 30 minutes.Reload to keep playing
NOT_SIGNED_INThe player isn't logged in.Log in to play
EMAIL_NOT_VERIFIEDPlayers must verify their email before spending coins.Verify your email
TIMEOUTNo answer in 10 s. The coin may or may not have gone in: check getBalance().Try again
ERRORAnything else.Try again

getBalance()#

Resolves with the player's coin balance (a number). Handy after a TIMEOUT.

submitScore(score)#

Reports a final non-negative safe integer for the current session. Resolves { ok: true, scoreId, saved, testMode, qualifies, replayed, rank, initials, needsInitials }, or { ok: false, reason }. saved is false in a test preview or mock mode. When a new score reaches the all-time top ten, the arcade opens the initials picker; saved initials are offered again on future scores.

The game submits an untrusted client report. Do not use this result to grant valuable game entitlements; use the server score endpoint in server verification for trusted scores.

game.js · runs and game over
/** Spend a credit to start a run; false when the player must insert a coin. */
export function startRun() {
  if (credits < 1 || paused) return false;
  credits -= 1;
  ui.credits.textContent = String(credits);
  return true;
}

export async function gameOver(score) {
  const result = await GemsArcadia.submitScore(Math.max(0, Math.floor(score)));
  if (!result.ok && result.reason !== "UNAVAILABLE") {
    console.warn("Score not saved:", result.reason);
  }
  return result;
}

unlockAchievement(key)#

Since 1.1.0. Unlocks one of your game's achievements (defined in the workshop's ACHIEVEMENTS tab) for the player, and the arcade plays the ACHIEVEMENT UNLOCKED celebration. Resolves { ok: true, achievement, unlocked, newlyUnlocked, unlockedAt, testMode } or { ok: false, reason }. Idempotent: repeats resolve with newlyUnlocked: false. Never rejects. Samples, fields and error codes are in the Achievements API.

ready()#

Tells the arcade your game has finished loading and can take coins.

on(event, callback) and off(event, callback)#

on returns an unsubscribe function.

EventPayloadWhen
coinInserted{ insertionId, balance }The player used the arcade's own INSERT COIN button. Grant a credit.
balanceChanged{ balance }The balance changed (a coin went in, or the player topped up).
pausenoneThe player switched tabs. Freeze the game.
resumenoneThe player is back.

mock#

GemsArcadia.mock.insertCoin(), .pause() and .resume() simulate the arcade in mock mode. They do nothing inside the real arcade. See testing & preview.

Under the hood#

Messages are { __ga: 1, id, type, payload }. The game posts requests (init, insertCoin, getBalance, submitScore, unlockAchievement, ready) to its parent with the arcade origin as targetOrigin. The arcade answers with type: "response", the same id and a payload of { ok: true, data } or { ok: false, error: { code, message } }, and sends type: "event" messages shaped { name, data }. The full walk-through is in coin flow.

REV 1.0 · KEEP THIS MANUAL WITH THE CABINET · QUESTIONS? ASK GLINT