Browser Storage APIs for Gaming: IndexedDB, Cache API, and File System Access
A developer's guide to all browser storage primitives available for gaming applications — capacity limits, performance characteristics, and when to use each one.
Browser gaming applications have storage requirements that differ significantly from typical web applications. A game needs to store large binary assets (ROM files, WASM modules, texture atlases), structured game state data (save files, settings, user preferences), and potentially multiple versions of emulation core binaries. The browser provides several distinct storage APIs for these purposes, each with different capacity limits, persistence guarantees, performance characteristics, and access patterns. Choosing the right storage API for each use case is an important architectural decision that affects both application performance and user experience reliability.
localStorage and sessionStorage: Not for Games
localStorage and sessionStorage are the most familiar browser storage APIs, available since IE8 and supported universally. They are synchronous, string-based key-value stores with a typical limit of 5–10MB per origin. Both limitations make them inappropriate for gaming use cases: synchronous access blocks the main thread (a problem for performance-sensitive code), string serialization adds overhead for binary data, and the 5MB limit is smaller than a single SNES ROM file.
The appropriate use of localStorage in gaming is limited to small text-based settings that must be read synchronously at startup — user display preferences, saved volume levels, a session token for quick authentication. For anything larger or more complex, IndexedDB is the correct choice. We mention localStorage only to explicitly rule it out as a general-purpose storage solution, because it is a common misuse pattern in browser game implementations.
IndexedDB: The Primary Game State Store
IndexedDB is the browser's primary structured data store: an asynchronous, transactional object database that supports binary data (ArrayBuffers, Blobs), structured cloning of complex JavaScript objects, indexes for efficient querying, and storage capacity limited only by available disk space (with a prompt to the user above a certain threshold, typically 50MB per origin). For game save states, user preferences, and metadata, IndexedDB is the correct API.
IndexedDB's transaction model provides important guarantees for save data integrity: a write transaction either completes fully or is rolled back, preventing partial saves. For a save state write that updates both the SRAM data and the save metadata record, wrapping both writes in a single transaction ensures that neither an SRAM file without updated metadata nor metadata without the corresponding SRAM file can exist in the store.
Performance characteristics to understand: IndexedDB is asynchronous and adds overhead compared to in-memory access, but modern implementations (especially Chrome's IndexedDB layer, which is implemented on top of LevelDB) achieve throughput of several hundred megabytes per second for bulk writes on desktop hardware. Read performance for keyed lookups is in the microsecond range. For gaming workloads where storage access happens at session boundaries (load at start, save at end), this performance is more than adequate.
The Cache API: For Immutable Assets
The Cache API was designed for Service Workers to cache network responses, but it is also available in regular window contexts as a content-addressable store for URL-keyed resources. For gaming applications, it is the ideal location for immutable or rarely-changing binary assets: ROM files, WASM modules, CDN-fetched game metadata, and cached artwork.
The Cache API's key advantage over IndexedDB for assets is its native URL-based caching semantics. Storing a ROM file by its canonical CDN URL means that cache lookups naturally coincide with network requests: before fetching from the network, the Service Worker checks the cache by URL. If the asset is present and the URL includes a content hash (as RetroCloud's URLs do), the cached version is definitively fresh and served without a network round-trip. This is the mechanism behind our sub-2-second game launch times for returning users with previously cached ROM files.
File System Access API: For Power Users
The File System Access API (formerly Native File System API) allows web applications to read from and write to the local file system with explicit user permission. This API is relevant for gaming applications that need to import ROM files from the user's local machine or export save data to a local file. Rather than requiring users to navigate a file upload dialog, the File System Access API allows the application to request a persistent file handle that can be used across sessions.
For RetroCloud, the primary use case is ROM import: users who own physical cartridges and have their own ROM dumps can drag-and-drop or use the file picker to import them into the platform without uploading to our servers. The file handle persists in IndexedDB (File System Access handles are serializable), allowing the application to re-read the local file in future sessions without requesting permission again. This creates a smooth import workflow that respects both user privacy and the legal requirement for users to source their own ROMs.
Storage Persistence and Eviction
The most important operational concern for browser storage in gaming is eviction. Browsers may evict non-persistent storage when disk space is low, without warning to the application. A user who returns to a game after a month of inactivity on a low-storage device may find their save states gone. The StorageManager API provides two mechanisms to address this: navigator.storage.estimate() returns current usage and estimated quota, and navigator.storage.persist() requests persistent storage designation — storage that the browser will not evict without explicit user action.
RetroCloud requests persistent storage on first session and displays a clear user-facing explanation of why we need it. Browser UI for persistent storage requests varies by browser — Chrome shows a browser notification, Firefox shows an in-page permission request — and grant rates in our telemetry are above 80%, indicating that users understand and accept the request when given a clear explanation. For users who decline or whose browsers do not support the API, we fall back to cloud sync as the authoritative save location, treating local storage as a performance cache rather than the source of truth.
Daniel Ko
Lead Infrastructure Engineer, RetroCloud
Daniel architects RetroCloud's multi-region cloud infrastructure and CDN strategy. He specializes in edge computing, distributed caching, latency optimization, and network architecture for high-throughput gaming workloads.