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.

Plugin persistence depends on each plugin’s serialize.withSave mode.

  1. withSave: true: plugin payloads are stored in story snapshots and therefore follow story history and save/load operations.
  2. withSave: false: plugin payloads are stored outside story slots and are included in export payloads.

This means withSave: true systems rewind when players move backward or load older saves, while withSave: false systems can remain cross-run (for example global settings or achievements).

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:

const saveResult = await engine.saveToSaveSlot(2);
if (!saveResult.success) showErrorFallback(saveResult.err);
const loadResult = await engine.loadFromSaveSlot(2);
if (!loadResult.success) showErrorFallback(loadResult.err);

Autosave slot shortcuts:

const autosaveResult = await engine.saveToSaveSlot();
if (!autosaveResult.success) showErrorFallback(autosaveResult.err);
const autosaveLoadResult = await engine.loadFromSaveSlot();
if (!autosaveLoadResult.success) showErrorFallback(autosaveLoadResult.err);

Load best available recent save:

const recentSaveResult = await engine.loadRecentSave();
if (!recentSaveResult.success) throw recentSaveResult.err;

Build save UIs with getSaves():

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

When the full story payload is needed (heavy), load it lazily:

for await (const save of engine.getSaves()) {
const data = await save.getData();
console.log(data.storyIndex, data.snapshots.length);
}

Delete saves:

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

For cloud backup/share flows:

const exportResult = await engine.saveToExport();
if (!exportResult.success) showErrorFallback(exportResult.err);
else saveExportedDataSomewhere(exportResult.data);
// later, same device or another device
const importResult = await engine.loadFromExport(exportResult.data);
if (!importResult.success) showErrorFallback(importResult.err);

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.