SDK

Saves and cloud data

Uploaded games can keep data in their own browser storage (localStorage, IndexedDB), but it stays in one browser on one device. SDK saves are recommended because they follow a signed-in player across devices and browsers. The SDK offers two ways to save, and both use the same storage underneath:

  • Save slots (load and save): one JSON document per slot, with revision checks. Best for a game's main progress.
  • Key-value data (data.getItem and data.setItem): string values by key, like localStorage. Best for settings, high scores and quick ports.

Tick This build integrates the Smaz Games SDK on the upload form, or account saves are turned off for your game.

Where progress goes

PlayerWhere it is saved
Signed in with a verified emailTheir Smaz Games account. Available on any device they sign in on
Guest, or email not yet verifiedThe player's browser on Smaz Games. Lost if they clear site data
Private previewThe previewer's browser, separate from live progress. Browser storage your game writes itself is shared with the live game in the same browser
Outside Smaz Games (local test mode)Your page's localStorage

Check cloudSupported from init() rather than assuming a signed-in player has account saves. When account saving is not available the SDK uses browser saves automatically; your code does not change.

Each game's saves are separate. Your game cannot read another game's saves or browser storage, and players' saves are never shared with developers.

Save slots

const save = await OpenArcade.load('progress');
// Empty slot: { data: null, revision: 0, schemaVersion: 1 }
// Saved slot: { data, revision, schemaVersion, updatedAt }
let state = save.data ?? { level: 1, coins: 0 };

state.coins += 10;
const result = await OpenArcade.save('progress', state, 1);
// { revision, updatedAt }
  • You must load a slot before you save it. The SDK remembers the revision you loaded.
  • Saves run one at a time, in order. You can call save again before the last one finishes.
  • Save at meaningful moments, such as the end of a level, a purchase in your game shop or a settings change, not every frame. Every save is a request to the site.

Conflicts

Each save sends the revision you loaded. If the player saved from another tab or device in the meantime, the save fails with status 409 instead of overwriting newer progress.

try {
  await OpenArcade.save('progress', state, 1);
} catch (e) {
  if (e.status === 409) {
    const latest = await OpenArcade.load('progress');
    state = mergeProgress(latest.data, state); // your rule, e.g. keep the higher level
    await OpenArcade.save('progress', state, 1);
  } else {
    showMessage('Progress could not be saved. It is still in this session.');
  }
}

The site keeps the three previous account saves for each slot so the support team can recover progress.

Schema versions

schemaVersion is your save format number, from 1 to 100,000. When you change the format, load the old data, convert it in memory and save it with the new number. An earlier version of your game can be restored after an update, so never delete data you do not recognise. Create a new save only when a slot is confirmed empty: a failed load is not an empty save.

Key-value data

data stores strings by key in a save slot named data. It loads the slot once, keeps a copy in memory and sends changes made close together as one save.

await OpenArcade.data.setItem('volume', 0.8);              // stored as '0.8'
await OpenArcade.data.setItem('settings', JSON.stringify({ hints: false }));
const volume = Number(await OpenArcade.data.getItem('volume') ?? 1);
await OpenArcade.data.removeItem('old-key');
const keys = await OpenArcade.data.keys();

If another tab or device changed the data slot, the SDK reloads it and applies your changes again, so changes to other keys are kept. Keys are 1 to 100 characters. Do not use the slot name data with load and save yourself.

Browser storage

Browser storage belongs to the browser, not to a Smaz Games account. Everyone who plays your game in that browser shares it, whether signed in, signed in to a different account or playing as a guest. Use it for device settings and caches, not for account progress. If you move a game from localStorage to data, do not copy old browser data into a signed-in player's account automatically; it may belong to someone else.

Guest progress

Guest and account progress are kept separate, so a player who signs in does not see guest progress automatically. guestSaveAvailable from init() tells you whether a guest save exists in the progress slot. If it does and cloudSupported is true, you can offer an import:

if (env.cloudSupported && env.guestSaveAvailable) {
  showButton('Move my guest progress to my account', async () => {
    const moved = await OpenArcade.importGuest('progress');
    state = moved.data;
  });
}

Import works only into an empty account slot and never overwrites account progress. The guest copy is removed after a successful import.

Limits

RuleLimit
Slot names1 to 40 letters, numbers, _ or -
Size per slot1MB of JSON
schemaVersionWhole number from 1 to 100,000
DataPlain JSON. No functions, circular objects or BigInt
Previous versions kept3 per account slot

Do not store passwords, email addresses, tokens or other personal data in saves.

Handling failures

  • If saving fails, keep progress in memory, tell the player accurately, and try again later. Never report a failed save as saved.
  • A request that times out after 15 seconds may still have succeeded. Reload the slot before saving again.
  • If the player signs in or out, the game frame reloads and init() reports the new state. Save before calling promptSignIn().

Testing checklist

  1. Progress survives a reload and appears in a second browser signed in to the same account.
  2. Two accounts on the same device never see each other's progress.
  3. Guest progress stays separate and imports only into an empty account slot.
  4. A save from a second tab is detected as a conflict, and neither state is lost silently.
  5. Failed loads, timeouts and an offline network leave the player's progress intact.