Modern web applications (Figma, Photoshop on the Web, client-side SQLite Wasm, local-first vector search) have outgrown localStorage and IndexedDB. By leveraging the Origin Private File System (OPFS) and synchronous FileSystemSyncAccessHandle inside Web Workers, web runtimes achieve native NVMe disk performance. This studio evaluates browser storage tiers, quota eviction rules, and zero-copy byte stream architectures.
1. Live Browser Storage Quota & Eviction Telemetry
Real-time inspection via navigator.storage.estimate() and persistence mode verification
0 MB Used
0 MBQuerying quota...
Persistence Mode:Best-Effort (Evictable)
Estimated Free Disk:—
OPFS Support:Supported (Window/Worker)
Chromium (Chrome / Edge): Grants up to 60% of available disk space per origin. Storage is shared across IndexedDB, OPFS, CacheStorage, and Service Workers.
Gecko (Firefox): Dynamic quota calculation up to 10% of free disk space per origin, with a global ceiling of 50% across all origins.
WebKit (Safari): Defaults to 1 GB per origin. Prompts user confirmation when an application exceeds 1 GB, with 7-day script-writable cookie expiration rules.
2. Web Storage Technology Comparison Matrix
Evaluate access latency, thread safety, serialization tax, and capacity limits
Storage Engine
Access Model
Worker Support
Throughput / Latency
Capacity Limit
Serialization Overhead
Best For
OPFS (SyncAccessHandle)
Synchronous in Worker
Web Worker Only
Ultra-High (Direct NVMe Byte Streams)
Full Origin Quota (Gigabytes)
Zero (Direct pointer / typed buffer)
SQLite Wasm, CAD/Figma, game state
OPFS (createWritable)
Asynchronous Streams
Window & Worker
High (Stream chunking)
Full Origin Quota
Low (Stream chunked)
Large file downloads, video export
IndexedDB
Asynchronous (Events/Promises)
Window & Worker
Moderate (~5ms to 20ms per txn)
Full Origin Quota
Medium (Structured Clone algorithm)
Indexed client state, offline documents
CacheStorage
Asynchronous (Promises)
Window & Worker (Service Worker)
High (Blob streaming)
Full Origin Quota
Zero for HTTP Responses
PWA assets, audio/video streaming
localStorage
Synchronous Blocking
Window Only (No Workers)
Poor (Blocks Main Thread UI)
5 MB Hard Ceiling
Extreme (JSON stringification)
Small UI theme toggles, auth tokens
3. Production Code Blueprints & Integration Guide
Raw OPFS SyncAccessHandle worker scripts, persistence requests, and SQLite Wasm setup
// Loading blueprint...
Frequently Asked Technical Questions
What is the Origin Private File System (OPFS) and why is it vastly faster than IndexedDB?+
The Origin Private File System (OPFS) is a high-performance private filesystem sandbox provided by the File System Access API. Unlike standard web storage (IndexedDB, localStorage), which requires structured cloning and serializing objects across IPC boundaries, OPFS grants direct, synchronous access to raw byte streams via FileSystemSyncAccessHandle inside Web Workers. WebAssembly modules (such as SQLite compiled to Wasm) can execute direct in-place reads and writes (read(), write(), truncate(), flush()) at specific byte offsets with microsecond latency, achieving near-native NVMe SSD speeds (hundreds of megabytes per second) with zero garbage collection overhead.
How do browser storage quotas and eviction policies work across Chrome, Firefox, and Safari?+
Browsers manage client storage under two modes: "Best-Effort" (default) and "Persistent". Under Best-Effort, when the host device runs low on disk space, the browser automatically purges origin data without user warning, typically following a Least Recently Used (LRU) algorithm. In Chromium (Chrome/Edge), an origin can consume up to 60% of total available disk space. In Firefox, an origin can consume up to 10% of total disk space (capped by group limits). In Safari / WebKit, storage is aggressively limited: origins are initially restricted to 1 GB, and Safari will prompt the user for permission before expanding. Calling navigator.storage.persist() requests persistent storage immunity from automatic LRU eviction.
Why does localStorage degrade application performance and cause frame drops?+
localStorage has three severe architectural bottlenecks: (1) Synchronous I/O on the Main Thread: Every read (getItem) and write (setItem) blocks the browser main thread. While waiting for disk I/O, the browser cannot render frames, process touch gestures, or execute animations, causing UI jank. (2) Strict String-Only Storage: Every object or array must be serialized to a JSON string, creating heavy CPU and memory churn. (3) 5 MB Storage Ceiling: Exceeding 5 MB throws a QuotaExceededError. Production web applications should never use localStorage for state caching; IndexedDB or OPFS should always be used asynchronously off the main thread.
How does SQLite Wasm use OPFS to achieve high-performance transactional persistence in the browser?+
The official SQLite WebAssembly distribution implements an OPFS Virtual File System (VFS) driver (sqlite3-opfs-async-vfs or the synchronous OPFS VFS). In a dedicated Web Worker, SQLite maps database pages (typically 4 KB) directly to a file within OPFS using FileSystemSyncAccessHandle. Transactional write-ahead logging (WAL) and atomic commits (fsync / flush()) map directly to the operating system filesystem calls. This enables complex relational SQL queries, joins, indexes, and full-text search directly inside client browsers at native speeds, powering modern local-first applications.
What is the operational difference between CacheStorage and IndexedDB?+
CacheStorage is specifically designed to store pairs of HTTP Request and Response objects, managed primarily by Service Workers for offline Progressive Web App (PWA) asset caching. It is optimized for streaming large binary Blobs, images, audio, video, and bundled JavaScript/CSS files. IndexedDB is a transactional NoSQL object store designed for structured, indexable application data (JSON objects, records, key-value trees). While you can store Blobs in IndexedDB, CacheStorage is substantially more efficient for HTTP response caching because Service Workers can return cached responses directly to fetch events via event.respondWith(caches.match(request)) without memory deserialization.