arcadible.saves
The game's saves, kept by the play page across versions. Each code a call fails with is in Codes, and the limits are in Limits.
interface Saves {
write(name: string, data: string): Promise<SaveInfo>;
read(name: string): Promise<string | null>;
readSync(name: string): string | null;
list(): Promise<string[]>;
listSync(): string[];
info(name: string): Promise<SaveInfo | null>;
infoSync(name: string): SaveInfo | null;
remove(name: string): Promise<void>;
readonly where: "game" | "build" | "memory" | string | undefined;
}
At startup, await arcadible.ready, then load with read, which waits for the saves even when they're late. During play, read with the sync calls: readSync, listSync, and infoSync answer at once from the SDK's copy of the saves in memory, and can't fail with busy or timeout. The async read, list, and info ask the play page each time.
Every call but the sync ones returns a promise, and can be made before ready: it waits for it. Names are 1 to 64 letters, digits, -, _, or .. Every call that takes a name rejects, or throws, with invalid for any other.
write(name, data)
write(name: string, data: string): Promise<SaveInfo>
Keeps data as the save name, replacing any save of that name. The promise resolves with the save's SaveInfo once it's kept on the device.
const info = await arcadible.saves.write("progress", JSON.stringify(progress));
- Fails with
invalid,too_many,too_large,full,busy,timeout, orunavailable. - After
ready, the save shows in the sync calls at once, in the same tick, before the promise settles. When the play page refuses it, it's taken back. datais a well-formed string. Serialize anything else, such as withJSON.stringify.- A write that fails with
timeoutmay still be kept: the sync calls keep showing it.
read(name)
read(name: string): Promise<string | null>
Resolves with the save, or null when there's none.
const saved = await arcadible.saves.read("progress");
const progress = saved ? JSON.parse(saved) : { level: 1 };
- Fails with
invalid,busy,timeout, orunavailable. - It waits for the saves, even when they're late. When they can't be reached, it fails with
timeoutorunavailable, which isn't the same as no save.
readSync(name)
readSync(name: string): string | null
Returns the save, or null when there's none, from the SDK's copy in memory, without waiting.
await arcadible.ready;
const settings = JSON.parse(arcadible.saves.readSync("settings") ?? "{}");
- Throws
not_readybeforeready,unavailablewhile the saves haven't loaded, andinvalidfor a bad name. - The copy includes this page's writes at once, and other tabs' as soon as they're kept.
- If the saves take longer than 10 seconds to load,
readysettles anyway, and the sync calls throwunavailableuntil they arrive, rather than answeringnullfor a save that exists.
list()
list(): Promise<string[]>
Resolves with the names of the game's saves, sorted.
const slots = (await arcadible.saves.list()).filter((name) => name.startsWith("slot-"));
- Fails with
busy,timeout, orunavailable.
listSync()
listSync(): string[]
Returns the names of the game's saves, sorted, from the SDK's copy in memory, without waiting.
await arcadible.ready;
const slots = arcadible.saves.listSync().filter((name) => name.startsWith("slot-"));
- Throws
not_readybeforeready, andunavailablewhile the saves haven't loaded, asreadSyncdoes.
info(name)
info(name: string): Promise<SaveInfo | null>
Resolves with the save's SaveInfo, or null when there's none.
const info = await arcadible.saves.info("progress");
// { version: "1.2.0", savedAt: "2026-10-12T18:21:24.692Z" }
- Fails with
invalid,busy,timeout, orunavailable.
infoSync(name)
infoSync(name: string): SaveInfo | null
Returns the save's SaveInfo, or null when there's none, from the SDK's copy in memory, without waiting.
await arcadible.ready;
const info = arcadible.saves.infoSync("progress");
- Throws
not_readybeforeready,unavailablewhile the saves haven't loaded, andinvalidfor a bad name, asreadSyncdoes. - For a write still waiting on the play page,
savedAtis this device's time until the page answers.
remove(name)
remove(name: string): Promise<void>
Removes the save name. Removing a save that doesn't exist isn't an error.
await arcadible.saves.remove("slot-3");
- Fails with
invalid,full,busy,timeout, orunavailable. - After
ready, the save is gone from the sync calls at once.
where
readonly where: "game" | "build" | "memory" | string | undefined;
Who can read the saves back, once ready has settled. It doesn't change while the page is open.
| Value | Who can read them back |
|---|---|
"game" | Every version of the game, in its channel: the play page keeps them, on the web, in the desktop app, or under arc serve. |
"build" | Only this build: the game stands alone, keeping them on its own origin. On Arcadible, every version has an origin of its own, so the next version won't see them. |
"memory" | Only until the page closes: the browser won't let them be kept, such as when it blocks site data. |
undefined | Not known yet: before ready, and while the saves haven't loaded. |
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".
SaveInfo
type SaveInfo = {
version: string | null;
savedAt: string;
};
| Field | Value |
|---|---|
version | The version of the game that wrote the save, stamped by the play page. null when written off Arcadible. |
savedAt | When it was written, as an ISO 8601 time. |
Compare version with arcadible.build.version to handle other versions' saves.