Design a browser-game save you can change later
A save file is an agreement with a future version of your game. Today's array of unlocked levels might become tomorrow's branching map. I'd rather make that agreement explicit while the save is small than discover that every player's progress depends on the order of an array you just rearranged.

Store durable meaning
Keep stable identifiers for progress. “garden-entrance completed” survives a reordered level menu better than “level 3 completed.” Avoid saving live engine objects. Store the minimum data needed to reconstruct the state you intend to preserve.
I would keep preferences separate from progression and both separate from a suspended round. They have different recovery costs. Losing a volume setting is irritating; losing a long campaign's progress is a much bigger failure. Your recovery message should reflect that difference.
Migrate before the game reads the data
Treat deserialization as a boundary. Parse the stored value, validate the version and shape, migrate known older versions, then hand a current-format object to the game. Gameplay code should not need to ask which historical save format it received.
Keep fixtures for old versions. If version one used numeric level positions, the migration needs the old position-to-ID map, not the current menu order. Ask an assistant to write the migration against those fixtures and explain what happens to an unknown future version. Silently overwriting a save created by a newer build can destroy information.
// A format sketch, not a storage adapter.
const save = {
version: 2,
completedLevelIds: ["garden-entrance"],
bestScores: { "garden-entrance": 420 },
settings: { muted: false }
};Plan for writes that fail
A browser storage call can fail or be unavailable. The game should have a defined response: continue without persistence, retry later or explain that progress wasn't saved. Do not report success just because you attempted the write.
For a small record, preserve the last valid value until a replacement is ready. For larger or multi-record state, use the storage mechanism's transaction facilities where appropriate. The exact adapter belongs to your engine and deployment target; the format contract should be testable without either.
My release checklist would include an empty save, malformed JSON, an old valid save, an unknown version and a failed write. I would also load the real hosted build, because editor success says little about browser storage behavior. A save system earns trust through recovery as much as through the happy path.
Sources & further reading
- Godot: your first 2D game — accessed Sep 9, 2026
Made something playable?
Share your game, world or experiment with people exploring what AI can help create.
Add your space to Velven