Saves for Games on Arcadible
Each version of your game has its own origin, so its browser storage starts empty with every update. Saves made through the Arcadible SDK are kept by the play page instead, so they carry over from one version to the next. They're kept on the player's device.
A save belongs to one player, in one browser or the desktop app: other players never see it, and it doesn't follow the player to another device. For data players share, such as scores or a shared world, use your own server. See Networking.
The Arcadible SDK is optional, and saves are all it does. Installing it, a full example, and its codes are in Arcadible SDK, and every call in arcadible.saves. This page is how to use saves well.
Add the SDK
Each kind of game has one way in:
| Your game | Add |
|---|---|
| A build step, like the Vite engine starters | The package: npm install @arcadible/sdk |
| No build step, like the blank starter | The hosted script, <script src="/arcadible/sdk/v1.js"></script> |
| An engine's web export, such as Unity or Godot | Not supported: the Arcadible SDK doesn't currently have a plugin for engines |
How to add each is in Install. arc serve runs your game locally with either, as Arcadible does; under another server, see Locally.
Save and load
A save is a name holding one string. Serialize it yourself:
// at startup: once the saves have loaded, the player's progress, or a new game.
await arcadible.ready;
const saved = await arcadible.saves.read("progress");
const progress = saved ? JSON.parse(saved) : { level: 1 };
// at a checkpoint:
await arcadible.saves.write("progress", JSON.stringify(progress));
- Start with
arcadible.ready, then load withread.readysettles once the saves have loaded;readwould wait for it anyway, and also waits for the saves when they're late. - Keep what must stay consistent in one save. Each save is written whole, at once, and saves never mix, so a level and the coins earned in it can't be torn apart. Use a save for progress, one for settings, and one for each slot, not one for each value.
- Save at checkpoints, and when the page is hidden, on
visibilitychangeandpagehide. A write resolves once the save is kept on the device, andreadSyncshows it at once, in the same tick, even before then. When a player closes the tab, the last write may not finish, so don't wait for the end to save. See Pause, save, and resume. - Moving from
localStorage, gather its keys into one object, and write it as one save.
A failed read isn't a missing save. When the saves can't be reached, read fails with timeout or unavailable. Don't start a new game and save over the player's progress: say so, and try again.
Read during play
arcadible.ready settles once, when the play page has answered and your game's saves have loaded, or after 10 seconds if they haven't. It never fails. From then on, the sync calls, readSync, listSync, and infoSync, answer at once from memory, so a read in the middle of a frame doesn't wait, and can't fail with busy or timeout:
// during play, after the startup above:
const settings = JSON.parse(arcadible.saves.readSync("settings") ?? "{}");
- Call anything before it. Calls made earlier wait for it. Only the sync calls throw before it, with
not_ready. - If the saves are late, taking over 10 seconds to load,
readysettles anyway, and the sync calls throwunavailableuntil they arrive, rather than answer as if there were no save.readwaits for them. arcadible.buildsays what's running: itsversion, fromarcadible.tomlorpackage.json, and itschannel. It'snulloff Arcadible.
Handle other versions' saves
Every version of your game shares its saves, so each version must read what the others wrote. The play page stamps each save with the version that wrote it, which infoSync returns:
const info = arcadible.saves.infoSync("progress");
// isNewer is your own semver comparison.
const fromNewer =
info?.version && isNewer(info.version, arcadible.build?.version);
Migrate older saves. Only your game knows its format, so read a save from an older version and update it.
Never overwrite a save from a newer version. Every version stays playable at its link, so it happens after you release an older version again, or when a player opens an older version's link. Play on without writing, or ask the player to update.
Channels
production and staging keep separate saves, so a prerelease can't touch players' progress. A version link uses its build's channel.
build.channel is a string, and other values may be added. Don't assume it's always one of the two.
Two tabs
A player can open your game in two tabs or windows at once. Each sees the other's saves as soon as they're kept, and the last write wins. Your game isn't told when the other tab saves.
- Read before you write. Build each save from
readSyncat the moment you save, not only from what you loaded at startup, or one tab overwrites the other's progress.
When a call fails
A call that fails rejects with an ArcadibleError. Act on its code, not its message:
- Worth retrying:
busy,timeout, andunavailable. A write that timed out may still have been kept. - Not worth retrying:
invalid,too_large, andtoo_many, which mean the save breaks a limit, andfull, which means the device has no room left. Tell the player. - A code you don't know: treat it as
unavailable. New codes may be added.
Every code is in the reference.
Where saves are kept
The play page keeps saves in its own browser storage, on the player's device. Arcadible doesn't currently keep them in the player's account, so they follow the rules of browser storage:
- Per browser and per app. A save in one browser isn't in another, or in the Arcadible desktop app.
- Cleared with the site's data. Clearing
play.arcadible.com's data clears every game's saves. - Evictable. Browsers can clear them when space runs low. Safari clears them after seven days of use without a visit to Arcadible (WebKit).
- Private windows keep them until the window closes.
Saves don't need the network: a game keeps saving offline for as long as it's open.
Who can read them back
saves.where says, once ready has settled:
"game": every version of your game can read them. The usual case, on Arcadible and underarc serve."memory": the browser won't let the play page keep them, such as when it blocks site data, so they last only until the page closes. Tell the player their progress won't be kept."build": only this build can read them. Off Arcadible, it's the package standing alone. On Arcadible, it means the build was opened directly at itsarcadible.ioaddress, or the play page didn't answer within about a second, so the game kept its saves on its own origin, where the next version can't see them. It's rare, and a reload fixes it; the SDK says so in the console.
await arcadible.ready;
if (arcadible.saves.where === "memory") {
showNotice("Progress won't be kept after you close the game.");
}
More values may be added. Treat a value you don't know as "game".
Locally
arc serve runs your game in a local play page, so saves work as on Arcadible, from the package or the hosted script. build is { version, channel: "local" }, and the local page keeps saves apart from Arcadible's, carrying them over from one build to the next. See arc serve's SDK section. With --url, the page your dev server serves gets the same saves through the package; the dev server doesn't serve the hosted script.
Under another server, such as your engine's dev server:
- The package stands alone, with the same rules and limits. There,
buildisnull, saves aren't stamped (info().versionisnull), and every project on the same origin, such aslocalhost:5173, shares one set of saves. - The hosted script isn't found, and
arcadibleis undefined. To run the game there too, guard each use withif (window.arcadible).
Checklist
- Adds the SDK the way its kind of game does, and runs locally under
arc serve - Starts with
arcadible.ready, loads withread, and doesn't start over when the saves can't be reached - Uses the sync calls only after
arcadible.ready - Tells the player when
saves.whereis"memory" - Keeps what must stay consistent in one save
- Saves at checkpoints and when hidden, without waiting for the player to leave
- Migrates older versions' saves, and never overwrites a newer version's
- Reads a save before writing it
- Treats a code it doesn't know as
unavailable - Keeps its saves across an update on staging: deploy a prerelease, save, deploy the next, and load
Next steps
arcadible.saves: every call, what it returns, and when it fails.- Arcadible SDK: installing it, a full example,
readyandbuild, and every code. - Limits: how much a game can save.