Skip to content
E
Egmatic
browser gamesweb game saveslocalStorageIndexedDBno backendindie game dev

How Do Browser Games Save Data Without a Server?

Browser games keep saves in localStorage and IndexedDB, storage the browser manages on the player's device. Here is how it works and when it fails.

Vladislav KovnerovSeptember 25, 202615 min

A browser game saves data inside the browser itself. The two mechanisms almost every web game relies on are localStorage, a small per-site key-value store, and IndexedDB, a much larger database the browser manages on the player's device. No server receives anything. A save survives restarts and works offline, but it belongs to one browser on one machine, and the browser reserves the right to delete it. Everything else in this article follows from those three properties: where the data physically sits, how much fits, and what makes it vanish.

The question keeps surfacing in the community reads we run, in two forms. Players ask how a game in a browser tab remembers anything between sessions at all. Developers ask where saves go when they never wrote a line of storage code, and why a save that worked on Tuesday is gone by Friday. When a single-file demo app that keeps all of its data in SQLite drew hundreds of upvotes on Hacker News this month, the discussion underneath was this same topic: how much can a web page reliably remember without a backend. A disclosure before we start: this blog belongs to Egmatic, a no-code 2D editor in pre-alpha, and what that has to do with saving data is near the end.

Quick answer

Your questionThe short version
Where does the save physically live?On the player's device, in storage the browser allocates per site
Which mechanism does a web game use?localStorage for a few megabytes of settings; IndexedDB for everything bigger
Does it survive a restart?Yes, until the player clears site data or the browser evicts it
Does it follow the player to another device?No. One browser profile on one machine only
What is the biggest silent risk?Safari deletes script-writable storage after seven days without site interaction

If you are here looking for a lost save: it is still on the machine, in the browser profile that made it, unless site data was cleared or the game ran in a private window. If you are building a web game or a playable demo: the sections below map the whole territory, from per-browser quotas to how Godot, Unity and Construct wire their save systems into browser storage, ending with a persistence plan that keeps these surprises out of launch week. Hosting the build itself is a separate question we covered in how to host a playable web demo.

The storage every browser hands a game

Browsers give every site a slice of private, per-site storage, scoped to the origin: the combination of protocol and domain the page was loaded from. Two consequences follow. The same game served from two different addresses has two separate save stores. And the game's storage is invisible to every other site, which is why one page cannot read another page's saves.

Inside that slice, three mechanisms matter:

localStorage is a simple store of string keys and values. It is synchronous, meaning a read or write completes before the next line of code runs, which makes it convenient and easy to abuse: a large write on the main thread stalls the game for everyone. It holds a few megabytes per site. That is plenty for settings, unlocks, a best score or a compact save blob, and web games have used it for exactly that since it standardized.

sessionStorage is the same interface with a shorter life: everything in it is discarded when the tab closes. Games occasionally use it for a run in progress, but it is not where saves live.

IndexedDB is a real database: asynchronous, indexed, capable of holding structured records and binary blobs, and allocated a far larger share of the device than localStorage. It is where engines persist file systems and where any save heavier than a settings blob ends up. Because it is asynchronous, writing to it never blocks the frame, at the cost of the callback dance every engine has already wrapped for you.

A fourth term you will meet in engine documentation is cookies. Cookies are not where modern saves live, but they appear in save-related troubleshooting because the browser settings that gate IndexedDB sit under the cookie controls, a point Godot's documentation makes explicitly.

How much fits, per browser

IndexedDB quotas are large and browser-specific. The reference numbers, from Google's web.dev documentation:

BrowserIndexedDB allowanceNotes
Chrome and ChromiumAn origin can use up to 60% of total disk spaceThe browser itself caps at 80%; incognito windows set to clear on close get roughly 300 MB
FirefoxThe browser may use up to 50% of free disk spaceA site group (a domain and its subdomains together) is capped around 2 GB
SafariAbout 1 GB in practiceNo official published quota; the player is prompted in 200 MB increments as the limit approaches

localStorage is the small sibling in every browser: single-digit megabytes per site, strings only. For a save that is a JSON blob of state, that is usually enough. For screenshots, replays, level packs or a long incremental game's history, it is not, and the correct answer is IndexedDB from the start rather than localStorage plus a compression hack.

Code can ask how much room is left: the StorageManager interface provides an estimate of usage and quota. The numbers it returns are estimates, not promises, which is an accurate summary of browser storage in general.

When browsers throw saves away

Browser storage is best-effort by default. The browser may delete a site's data when the device runs low on space, and Chromium's documented eviction order is unforgiving: it clears the least recently used origin first, then the next, until the pressure passes. A player who has not opened your game in months is, by that rule, your most evictable player.

Four failure modes account for most lost saves:

  1. Clearing site data. The "clear cookies and site data" control removes localStorage and IndexedDB together. Players running storage cleaners hit this regularly.
  2. Private windows. Incognito and private mode discard all storage when the window closes. Godot's documentation states plainly that private browsing prevents persistence.
  3. Storage eviction. Disk pressure plus best-effort status equals deletion without anyone choosing to delete anything. A site can request persistent storage, which asks the browser to keep its data and clear best-effort sites first. If your engine or template exposes that switch, and your game has real saves, turn it on.
  4. Safari's seven-day cap. Safari deletes all of a site's script-writable storage, IndexedDB and localStorage included, after seven days of Safari use without interaction on that site. Web apps the user has added to the home screen are exempt, running with their own storage. For a game someone plays weekly, the cap never bites. For a game someone meant to come back to, the save is gone when they do.

None of these are bugs. They are the terms of the free storage the browser provides, and the reason games with serious progression eventually add accounts and cloud sync.

How engines map their save systems onto browser storage

Engine exporters translate their native save mechanisms into browser storage, so the same rules apply one level up:

EngineWhere saves go on the webWhat to watch
GodotThe user:// file system is persisted through IndexedDBPersistence requires cookies (specifically IndexedDB) to be allowed; a game shown in an iframe also needs third-party cookies; private windows prevent persistence
UnityPlayerPrefs are stored through IndexedDB, up to 1 MB on web buildsFile saves written to the player's persistent data path land in browser storage, inside the same quota and eviction rules
Construct 3The Local Storage plugin keeps all saved data in an internal browser database, producing no discoverable files on diskDespite the name it is a full asynchronous storage system; IndexedDB is available for advanced cases
Plain JavaScript and frameworksWhatever the code calls: localStorage for small values, IndexedDB for the restEvery quota and eviction rule in this article applies unmediated

Two practical notes follow from the Godot row. Because persistence rides on cookie settings, a player who blocks cookies entirely will play your web build saveless, usually without knowing why. And because portals embed games in an iframe on the portal's domain, the iframe caveat is the normal case there, not the edge case.

Portals: your saves live on their domain

Storage is scoped to the origin the game was served from. On a portal, that origin belongs to the portal. Three consequences:

  • The save attaches to the portal's domain, not to you or to the player's account on your site. A player who later finds the same build on your own page starts from zero.
  • Every game the portal serves under that arrangement shares the portal's storage neighborhood, which is one reason to give your storage keys a distinctive prefix rather than a generic one like "save".
  • Iframe rules land here first. A portal player with third-party cookies blocked can lose persistence entirely, per the engine documentation above.

None of this makes portals a bad hosting choice; it makes them a different persistence context. Test your save system inside the portal's embed, not only in a local export, because the two environments are not the same storage world.

The SQLite-in-the-browser pattern

There is a newer option when a game's data outgrows key-value storage. SQLite compiles to WebAssembly and can keep an entire database in the Origin Private File System, a persistent, origin-scoped area of browser storage designed for exactly this. One constraint defines the pattern: the private file system is available in worker threads, not on the main UI thread, so the database work happens off the frame loop, which is where it belongs anyway.

The pattern reached a wide audience this month when Capsule, a tool for building single-file web apps that keep their data in SQLite, drew around 380 upvotes on Hacker News. The appeal is straightforward: a full relational database over megabytes of state, queryable, exportable as one file, and still inside the browser's origin rules. For a save-heavy incremental or RPG running on the web, it is the current ceiling of no-backend persistence.

The ceiling has a weight limit, and it is the same one: SQLite in the browser lives in origin storage. Quotas, eviction, the seven-day Safari cap and the clear-data button all still apply. A better engine for the data does not change the terms of the land it sits on.

A persistence plan that survives contact with players

  1. Keep the save small and version it. A schema version number in the save blob costs one field and saves your update pipeline. What to store and when is a design question with its own guide: how to design a save system.
  2. Prefix your storage keys with the game's name. On any shared origin, and there are more of them than you think, generic keys collide.
  3. Add export and import of the save file. One JSON download button rescues every failure mode above. Players who lose saves to eviction or a Safari cap can restore from a file, and support turns into a sentence instead of an investigation.
  4. Test in a private window and after clearing site data. Those are the two states your players will actually be in when something goes wrong, and both are reproducible on demand.
  5. Request persistent storage if saves matter. If your engine exposes the setting, enable it; if you control the code, one call asks the browser to exempt the game from best-effort eviction.
  6. Read cross-device demand as the signal it is. When players ask for phone-to-desktop progress, the browser alone cannot answer. That is the moment for accounts and cloud saves, and there is a no-backend way to add them. The same origin storage also holds the scores web demos usually hand-roll first; adding leaderboards without a backend covers that half of the problem.

Common mistakes

  • Stuffing megabytes into localStorage. The write fails when the quota is hit, and the failure usually reaches the developer as a player report, not an error message.
  • Assuming the save follows the player. Another browser, another device, another machine: from the storage's point of view, another person.
  • Testing only in Chrome. Safari holds both the strictest cap and the least official documentation; it is where saves actually die.
  • Trusting a portal embed with long-term progression. The iframe cookie rules apply there first, and the player has no relationship with the origin holding the data.
  • Shipping a save without a version field. The update arrives, old saves parse halfway, and the bug reports describe symptoms three steps removed from the cause.
  • Counting on incognito playthroughs to persist. Private windows are popular for second runs of web games, and everything in them is temporary by design.

How Egmatic fits

Everything above is plumbing. Knowing IndexedDB from localStorage makes you a better web developer; it does not make your game better designed. The decision that deserves your attention is what the game remembers and for whom, and the rest is a layer a tool should carry.

Egmatic is a no-code 2D editor and engine in pre-alpha, built around a ship layer that treats persistence as part of publishing: the same pipeline that carries a game from scene to hosted link is being designed to carry its saves, from local storage through accounts and cloud sync, so that where saves live becomes a setting rather than a research article. You direct what the game remembers, the editor executes it, and every file stays yours.

We will state the stage plainly: Egmatic is pre-alpha, and we do not announce dates we cannot keep. The waitlist at egmatic.com is where the persistence layer takes shape, and telling us there what your web game needs to remember is the input that steers it.

Saves that live where your game lives.

Persistence should be a decision you make, not plumbing you hand-roll. Egmatic is a no-code 2D editor with a ship layer that treats saves, accounts and cloud sync as part of publishing. Reply on the waitlist with what your web game needs to remember: requests sent during pre-alpha are the ones that steer the build.

No spam. Unsubscribe anytime.

Sources

  1. web.dev — Storage for the web: Chrome allowing an origin up to 60% of total disk space with the browser capped at 80%; incognito windows set to clear on close limited to roughly 300 MB; Firefox allowing the browser up to 50% of free disk space with an eTLD+1 site group capped around 2 GB; Safari allowing about 1 GB with prompts in 200 MB increments; best-effort as the default storage mode; Chromium evicting the least recently used origin first under storage pressure; persistent storage as the opt-out; Cache Storage recommended for network resources rather than user data
  2. WebKit Blog — Full Third-Party Cookie Blocking and More: the seven-day cap on all script-writable storage, IndexedDB, LocalStorage, SessionStorage and Service Worker registrations among the listed types, applied after seven days of Safari use without user interaction on the site; web applications added to the home screen keeping their own separate storage
  3. MDN Web Docs — Web Storage API: localStorage and sessionStorage storing strings per origin; both APIs synchronous in nature
  4. MDN Web Docs — Storage API: the StorageManager interface providing storage estimates; "persistent" buckets retained as long as possible, with best-effort buckets cleared first under storage pressure
  5. Godot Engine documentation — Exporting for the Web: persistence of the user:// file system requiring cookies, specifically IndexedDB, to be allowed; games presented in an iframe additionally requiring third-party cookies; incognito and private browsing preventing persistence
  6. Unity Script Reference — PlayerPrefs: web builds storing up to 1 MB of PlayerPrefs data using the browser's IndexedDB API
  7. Construct 3 Manual — Local Storage plugin (read via archive.org snapshot of 7 March 2026, the live page renders through scripts): all saved data kept in an internal browser database producing no easily discoverable files on disk; IndexedDB available for more advanced cases
  8. SQLite documentation — Persistence for the sqlite3 WASM/JS build: the Origin-Private FileSystem as an API providing browser-side persistent storage usable for SQLite databases; OPFS available in Worker-thread contexts, not the main UI thread
  9. Hacker News — Show HN: Capsule, single-file web apps that save their data into SQLite: approximately 380 points, read 25 September 2026; the community discussion of web apps keeping structured data without a backend

Related Posts