Why we built this
Local CDN was built by web developers who love the web platform but need native power. We wanted offline media that works the way web developers expect: set asrc attribute, use fetch(), work with standard URLs. No Base64 encoding. No Blob URL workarounds. No arbitrary storage limits.
So we collaborated with native iOS and Android engineers. We studied how Spotify, Netflix, and YouTube handle offline downloads in their native apps. Then we brought that same architecture to hybrid web apps while keeping everything familiar to web developers.
The result: cache gigabytes of media with two function calls, play it back with a standard URL, and maintain O(1) memory usage independent of file size.
Cross-platform by design
Local CDN is optimized for both iOS and Android through a unified JavaScript SDK. Write your code once. It works identically on both platforms. No iOS-specific workarounds. No Android edge cases. The API is fully standardized so you can focus on building your app instead of handling platform differences.Fully client-side. No cloud dependencies.
Local CDN runs entirely on the device. There is no cloud component, no proprietary backend, no special hosting requirements. Built on Despia Local Server. Despia Local Server provides the HTTP infrastructure - a proprietary, production-grade localhost server that runs entirely within your app. Local CDN extends this by adding file storage and content serving capabilities through the native file system. Together, they form a complete on-device content delivery system. Fully maintained, fully client-side. Both Despia Local Server and Local CDN are compiled into your app binary. No external services, no cloud dependencies, no opinionated backend architecture forced on you. Everything runs on the device. Use any hosting. Cache files from any URL - your own servers, AWS S3, Cloudflare, Google Cloud Storage, or any public CDN. Local CDN does not care where files come from. No cloud lock-in. No data lock-in. Your files are standard files on the device file system. Your URLs are standard HTTP URLs. Your backend, your database, your API - all remain yours. Despia never sits between you and your infrastructure. Runtime dependency only. Local CDN and Despia Local Server are runtime dependencies, similar to any other SDK you include in your app. The proprietary component is the on-device localhost server and native file system integration. Your data formats, hosting choices, and backend architecture remain portable. No per-user fees. Unlike cloud-based solutions that charge per monthly active user, Local CDN has zero runtime costs. Scale to millions of users without infrastructure costs scaling with them. Works offline completely. Once files are cached, the entire system operates without any network connectivity. No license checks, no heartbeats, no server dependencies.Despia Local Server: The complete picture
Local CDN is one part of the Despia Local Server ecosystem. The full Local Server provides: Instant boot. Zero network latency. Your entire web app loads in milliseconds from localhost. Complete offline support. Not “works offline sometimes” but actually works without any connectivity, forever. OTA updates. Push web app changes live without app store approval. Updates download in the background and apply on next launch. Your hosting, your rules. Keep using Netlify, Vercel, AWS, whatever you already have. No migration, no cloud lock-in, no MAU fees. 100% store compliant. Fully approved architecture for both Apple App Store and Google Play Store. Local CDN handles media and file caching. Despia Local Server handles your entire web application. Together, they give you native-quality performance with web development workflows. For complete documentation on Despia Local Server including setup, OTA updates, and architecture details, see: How Local Server WorksWho this is for
Local CDN solves a specific problem for a specific type of app. It’s not for everyone. This is for you if:- Your app caches large media files (videos, podcasts, audio courses, image libraries)
- You need true offline playback, not just “works offline sometimes”
- Memory constraints are a real problem - your app crashes or lags with current caching approaches
- You’re building for both iOS and Android and want one solution
- You’re comfortable with a runtime dependency in exchange for solving a hard problem
- Your app only caches small files (under 50MB total)
- IndexedDB or browser cache already works fine for your use case
- You prefer open-source solutions with no proprietary dependencies
- You need to minimize third-party SDKs
- Your team isn’t hitting memory or storage limits with current approaches
The 15-year problem
Since PhoneGap launched in 2009, hybrid web-native apps have struggled with offline media. The core issue: WebViews run in a sandboxed environment with no direct file system access. Every framework has attempted to solve this. None have done it well. Cordova required plugins and Blob URLs. Files had to be read into JavaScript memory before playback. Ionic inherited Cordova’s limitations. Developers worked around them with complex caching strategies that still hit browser storage quotas. Capacitor improved file system access but still requires Base64 encoding to pass data through the JavaScript bridge. A 500MB video requires approximately 1.5GB of memory during the read-decode-play cycle. React Native offers partial solutions, but media handling remains complex and platform-specific. The fundamental problem across all these frameworks: everything passes through JavaScript. Files must be encoded, transferred across the bridge, decoded, and converted to Blob URLs before playback. Memory usage scales with file size. Large files crash apps. Web developers have been forced to choose between streaming everything (no offline support), caching small files only (poor user experience), or abandoning web technologies entirely.How other frameworks handle it
Capacitor’s approach
- Base64 encoding adds 33% size overhead
- Entire file must load into JavaScript memory
- Blob URL creation duplicates the memory reference
- Large files cause out-of-memory crashes
- Platform-specific quirks require additional handling
Framework comparison
| Framework | File System Access | Direct Playback | Memory Efficient | Cross-Platform | Offline Media |
|---|---|---|---|---|---|
| Cordova | Plugin required | No | No | Partial | Limited |
| Capacitor | Yes | No | No | Partial | Limited |
| React Native | Yes | Partial | Partial | Partial | Complex |
| Flutter | Yes | Yes | Yes | Yes | Not web-based |
| Despia Local CDN | Yes | Yes | Yes | Yes | Full support |
Our approach
Local CDN bypasses JavaScript entirely for file operations. When you cache a file, Despia’s native layer (Swift on iOS, Kotlin on Android) downloads it directly to the device file system. The file never touches the JavaScript bridge. When you play it back, Local CDN serves the file through Despia Local Server viahttp://localhost. Your web app uses a standard URL - the same way you would load any media from a remote CDN.
src attribute.
Built for web developers
We kept everything familiar to web developers: Standard URLs. Thelocal_cdn value is a regular HTTP URL. Use it anywhere you would use a CDN URL: src attributes, fetch() calls, CSS url() values.
No encoding. Files stay as files. No Base64. No ArrayBuffers. No Blobs.
No new APIs to learn. If you know how to set videoElement.src, you know how to use Local CDN.
Familiar patterns. Write is like uploading to a CDN. Read is like querying a database. The response is JSON.
Technical architecture
Write operation
When you calllocalcdn://write:
- Despia’s native layer receives the request
- Native HTTP client downloads the file directly to device storage
- File is saved to the app’s dedicated cache directory
- Metadata (path, size, extension, original URL) is indexed
- JavaScript heap remains untouched
Read operation
When you calllocalcdn://read:
- Despia looks up the requested files in the metadata index
- Returns file information including the
local_cdnURL - Local CDN serves files through Despia Local Server via
localhost - Media elements stream directly from disk
- Hardware decoders handle playback with native performance
Why localhost
Despia Local Server provides the HTTP infrastructure. Local CDN stores files and serves them through this infrastructure viahttp://localhost. This architecture provides several advantages:
- Standard web APIs work without modification
- No CORS restrictions (same-origin)
- HTTP range requests enable seeking without loading entire files
- Localhost is treated as a secure context
- Hardware-accelerated decoders work with HTTP sources
Memory comparison
What O(1) means
O(1) means “constant” - the memory usage stays the same regardless of input size. Whether you play a 10MB file or a 10GB file, Local CDN uses the same amount of memory. This is the opposite of traditional approaches where memory scales with file size (O(n)). Double the file size, double the memory usage. That’s why large files crash apps.500MB video: Capacitor vs Despia
Let’s trace what happens when you play a 500MB video file. Capacitor (traditional approach):| Metric | Capacitor | Despia Local CDN |
|---|---|---|
| Peak JS heap | ~1,666MB | ~100 bytes |
| System memory | ~1,666MB | ~5-8MB |
| Scales with file size | Yes | No |
| 2GB video | Crash | Works |
Where the ~2-8MB comes from
Local CDN doesn’t use zero memory - that would be physically impossible. Here’s what actually uses memory during playback: Kernel read buffer (~1-2MB): The operating system reads files in chunks, not byte-by-byte. This buffer exists for any file read operation on any platform. Media pipeline buffer (~2-4MB): iOS and Android media frameworks buffer a few seconds of decoded frames for smooth playback. This is handled entirely in native code. Hardware decoder buffer (~1-2MB): The video decoder chip has its own memory for processing frames. This is separate from app memory. These buffers exist in native apps too - Spotify, Netflix, and YouTube all have them. The key difference: none of this memory is in your JavaScript heap, and none of it scales with file size. A 100MB video and a 2GB video use the same ~5-8MB of buffer memory. That’s what O(1) means in practice.Use cases
Media-heavy applications
Podcast apps can cache entire libraries. Video learning platforms can download courses for offline viewing. Music apps can store playlists locally. All without memory constraints.Offline-first experiences
Field service apps, travel apps, and educational tools that must work without connectivity can cache all required media assets during initial sync.Performance-critical applications
Localhost serving eliminates network latency entirely. Media plays instantly. Seeking is immediate. The experience matches native apps.Frequently asked questions
How much storage can I use? The device’s available storage. No artificial quotas like IndexedDB. Cache gigabytes of content. Does this work offline? Yes. Files are stored on the device file system and served from an on-device HTTP server. No network required after initial download. What file types are supported? Any file type: video (mp4, webm, mov), audio (mp3, wav, aac), images (jpg, png, webp), documents (pdf, json), and more. Is this App Store compliant? Yes. Downloading and caching media content is standard app behavior, identical to how Spotify, Netflix, and YouTube handle offline downloads. What happens if storage is full? The download will fail. Implement cache management logic to remove old files before downloading new ones. Does it work the same on iOS and Android? Yes. The JavaScript API is identical on both platforms. Despia handles platform-specific implementation details internally.Summary
| Capability | Traditional Web | Local CDN |
|---|---|---|
| Memory usage | Scales with file size | O(1) fixed |
| Storage limit | 50-100MB | Device capacity |
| Offline support | Unreliable | Complete |
| Playback performance | Software decoded | Hardware accelerated |
| Cross-platform | Platform quirks | Unified SDK |
| Cloud dependency | Often required | None |
| Data portability | Varies | Full (standard files, standard URLs) |
| Implementation | Complex | Two function calls |
| Large file support | Crashes | Works |