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.
What gets persisted
Section titled “What gets persisted”A story save contains data needed to restore narrative execution, including:
- state and state history
- current passage position
- metadata used for deterministic replay
Plugin-level persistence concerns (like settings/achievements) are documented in Plugins.
Plugin persistence and timeline behavior
Section titled “Plugin persistence and timeline behavior”Plugin persistence depends on each plugin’s serialize.withSave mode.
withSave: true: plugin payloads are stored in story snapshots and therefore follow story history and save/load operations.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).
Configure persistence
Section titled “Configure persistence”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.
Save slots
Section titled “Save slots”Choicekit supports:
- autosave slot
- numbered slots (
0tosaveSlots - 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;Listing and deleting saves
Section titled “Listing and deleting saves”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(); // autosaveawait engine.deleteSaveSlot(3); // slot 3await engine.deleteAllSaveSlots();Export and import
Section titled “Export and import”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 deviceconst 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.
Compression and save size
Section titled “Compression and save size”config.compress enables compact save payloads (recommended for large state/history).
Also keep vars focused on story facts, not transient UI state.
Save lifecycle events
Section titled “Save lifecycle events”You can hook save UX indicators and error handling with:
saveStart/saveEndloadStart/loadEnddeleteStart/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);});Common pitfalls
Section titled “Common pitfalls”- Missing persistence adapter: save/load/delete operations will fail.
- Changing state schema without migrations: old saves may become incompatible.
- Overloading vars with UI-only data: save payloads grow and become harder to evolve.
Next: see Save Migrations for versioning strategy.