Skip to content

Saving and Loading Saves

Saving in Choicekit is about preserving a playable run exactly as the player left it: passage, state history, and deterministic randomness context.

In IF terms, a save is a branch checkpoint.

A story save contains data needed to restore narrative execution, including:

  1. state and state history
  2. current passage position
  3. metadata used for deterministic replay

Plugin-level persistence concerns (like settings/achievements) are documented in Plugins.

Use a persistence adapter through engine config.

import { ChoicekitEngineBuilder } from "choicekit";
import { LocalStoragePersistenceAdapter } from "choicekit";
const engine = await new ChoicekitEngineBuilder()
.withName("daves-adventure")
.withPassages({ name: "Start", data: "..." })
.withVars({ gold: 0 })
.withConfig({
persistence: LocalStoragePersistenceAdapter,
saveSlots: 10,
autoSave: "passage",
})
.build();

Use a stable, unique engine name per story. Engines sharing a name share persisted keys.

Choicekit supports:

  1. autosave slot
  2. numbered slots (0 to saveSlots - 1)

Manual save/load:

await engine.saveToSaveSlot(2);
await engine.loadFromSaveSlot(2);

Autosave slot shortcuts:

await engine.saveToSaveSlot();
await engine.loadFromSaveSlot();

Load best available recent save:

await engine.loadRecentSave();

Build save UIs with getSaves():

for await (const save of engine.getSaves()) {
if (save.type === "autosave") {
console.log("autosave", save.data.savedOn);
} else {
console.log("slot", save.slot, save.data.savedOn);
}
}

Delete saves:

await engine.deleteSaveSlot(); // autosave
await engine.deleteSaveSlot(3); // slot 3
await engine.deleteAllSaveSlots();

For cloud backup/share flows:

const exported = await engine.saveToExport();
// later, same device or another device
await engine.loadFromExport(exported);

This is useful for player support, migration between devices, and beta testing reproduction.

config.compress enables compact save payloads (recommended for large state/history).

Also keep vars focused on story facts, not transient UI state.

You can hook save UX indicators and error handling with:

  1. saveStart / saveEnd
  2. loadStart / loadEnd
  3. deleteStart / deleteEnd
engine.on("saveStart", ({ slot }) => {
console.log("saving", slot);
});
engine.on("saveEnd", (event) => {
if (event.type === "success") {
console.log("saved", event.slot);
return;
}
console.error("save failed", event.slot, event.error);
});
  1. Missing persistence adapter: save/load/delete operations will fail.
  2. Changing state schema without migrations: old saves may become incompatible.
  3. Overloading vars with UI-only data: save payloads grow and become harder to evolve.

Next: see Save Migrations for versioning strategy.