LocalStorage Game Save in Phaser: A Working Example

LocalStorage Game Save in Phaser: A Working Example

Direct answer: Use localStorage for anything small and key-based — best scores, settings, unlocked levels, per-mode stats. Wrap it in one Storage module with typed getters/setters instead of calling localStorage.setItem all over your scenes. It syncs automatically to Phaser’s this.registry if you need reactive updates.

Merge Fish 2048 saves all persistent state this way. Here is the pattern that works.

1. The key map: one source of truth

Never scatter string keys through your code. Define them once:

export const STORAGE_KEYS = {
  BEST_SCORE: 'mergefish_best_score',
  MUSIC_ON:   'mergefish_music_on',
  SFX_ON:     'mergefish_sfx_on',
  GAME_STATS: 'mergefish_game_stats', // per-mode play counts
};

A typo like mergefish_best_socre silently creates a second, empty key and your “best score” feature just stops persisting. Centralizing keys turns that class of bug from invisible into impossible.

2. The Storage wrapper

export const Storage = {
  get(key, fallback = null) {
    try {
      const v = localStorage.getItem(key);
      return v === null ? fallback : JSON.parse(v);
    } catch { return fallback; }
  },
  set(key, value) {
    try { localStorage.setItem(key, JSON.stringify(value)); }
    catch { /* private mode / quota — fail soft */ }
  },
};

Two details matter:

3. Where it hooks into gameplay

4. When localStorage is not enough

NeedUse
Small key-value statelocalStorage (this article)
Bigger structured data (levels, map saves)IndexedDB
Sync across devicesA backend / leaderboard API
Save encryptionNot worth it client-side — see our encryption article

If you store images or entire level maps, localStorage’s 5MB limit will bite. For scores and settings, it is exactly right.

Common mistakes


Written by ruofan, independent HTML5 game developer. Keys and patterns come from Merge Fish 2048 (Phaser 3.90 + Vite). API reference: MDN localStorage.