Skip to main content

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, or unavailable.
  • 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.
  • data is a well-formed string. Serialize anything else, such as with JSON.stringify.
  • A write that fails with timeout may 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, or unavailable.
  • It waits for the saves, even when they're late. When they can't be reached, it fails with timeout or unavailable, 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_ready before ready, unavailable while the saves haven't loaded, and invalid for 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, ready settles anyway, and the sync calls throw unavailable until they arrive, rather than answering null for 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, or unavailable.

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_ready before ready, and unavailable while the saves haven't loaded, as readSync does.

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, or unavailable.

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_ready before ready, unavailable while the saves haven't loaded, and invalid for a bad name, as readSync does.
  • For a write still waiting on the play page, savedAt is 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, or unavailable.
  • 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.

ValueWho 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.
undefinedNot 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;
};
FieldValue
versionThe version of the game that wrote the save, stamped by the play page. null when written off Arcadible.
savedAtWhen it was written, as an ISO 8601 time.

Compare version with arcadible.build.version to handle other versions' saves.