What it does
read(key, fallback) returns the stored value, or the fallback when the key is missing or its text is not valid JSON,
typed as the fallback. write(key, value) stores the value as JSON. keys, remove and clear work within the
game's namespace and leave other namespaces alone, so clear wipes one game's data and nothing else.
Browsers do not always keep data: a private window, a full quota or a headless test run can all refuse. When a storage
operation fails, the session switches to an in-memory copy of everything it had, and reads, writes and deletions carry
on against it. persistent turns false, so a game can show "scores not saved" rather than lose them without a word.
Features
- JSON values — numbers, strings, arrays and objects under a key, typed by the fallback you read them with.
- A namespace per game —
namespaceprefixes every key; the default isphaser-ae. - A graceful fallback — localStorage when it is there, memory when it is not, with existing data mirrored so nothing already stored goes missing for the session.
- A persistence flag —
persistentsays whether this session's writes will survive a reload. - Loud on bad values — a cycle, a BigInt or a value with no JSON form throws a
TypeErrorbefore anything changes. - Reachable from other packs — through the registry, so a pack can save a value without importing this one.
Good for
- High-score tables in arcade games.
- Options screens for music, sound and controls.
- Unlocked levels and worlds in a level-based game.
- Checkpoints and continue screens, with the kernel's scene flow.
Make it yours
There is one option, namespace: give each game its own, as { "id": "save", "options": { "namespace": "my-game" } },
or two games on the same site share a store. Two different uses: an arcade game that keeps a single best score and
shows it on the title screen; and a puzzle game that saves a settings object and a list of finished levels, reading
each with a default so a first run starts clean.
A quick look
A best score, a settings object and a warning when nothing will be kept:
const best = game.save.read('best', 0);
game.save.write('best', Math.max(best, score));
const settings = game.save.read('settings', { music: 0.5, sfx: 1 });
game.save.write('settings', { ...settings, music: 0 });
if (!game.save.persistent) game.typography.label({ x: 8, y: 8, text: 'SCORES NOT SAVED' });
Works with
core-2d,core-3dandcore-vector— all three include it, so most games have it already.typography— for the score display.renovation-progress— versioned saves of a whole economy, for progress that must survive catalogue changes.
Good to know
- Values must survive
JSON.stringify: no functions,Map,Setor cyclic objects. - Every write is a JSON string in a store browsers cap at a few megabytes, so it is not for per-frame state or large data, and not for replays: the kernel's own record is the replay.
- Memory-only values vanish on reload, and two tabs on one site do not see each other's writes until they read again.
- There is no encryption or signing: a player can edit their own scores.






