Why the Web Must Survive the Network

Zach Jensz opens with a camping scenario that exposes how poorly ordinary websites handle lost or unreliable connections. He shows how HTTP caching can preserve previously loaded pages, then explains why browser eviction, hard reloads, private browsing, and cold navigation make the default cache too unreliable for a complete offline experience.

Taking Control with Service Workers

Jensz introduces pre-caching as a way to keep saved recipes and recommendations available before the network disappears. He explains the service worker lifecycle, request interception, Cache API access, cache versioning, client control, and the secure-connection requirement.

From Website to Installable Offline App

A web app manifest gives the recipe site a home-screen icon, standalone presentation, launch URL, icons, and theme settings, allowing it to behave more like a native app. Jensz then contrasts online-first loading with real-world connectivity, where a device may report itself online while the wider internet remains unreachable—a condition Jake Archibald calls “Li-Fi.”

Offline-First Loading and Storage Limits

Jensz argues that cached assets improve performance even for business users on fast connections by avoiding latency and TCP slow start. He demonstrates a cache-first strategy, recommends Workbox for production implementations, reviews browser support and storage quotas, and weighs the cost, sustainability, privacy, and fairness of aggressive pre-caching.

Local Databases and Background Synchronization

The recipe example expands into structured offline data, including comments, sorting, and server synchronization. Jensz surveys IndexedDB, the IDB wrapper, the Origin Private File System, SQLite compiled to WebAssembly, and Background Sync for deferred uploads and downloads, while emphasizing visible notifications and user control.

PWAs, Browser Competition, and Progressive Enhancement

Jensz compares uneven browser support for notifications and background capabilities, framing the disagreement as both a standards question and a competition problem. He argues that PWAs can provide one installable codebase across devices, immediate cached loading, direct updates, and progressively enhanced features without requiring every browser to implement them at once.

Running AI Locally in the Browser

Jensz turns to offline AI as a way to avoid API keys, server inference costs, and data leakage by keeping computation on the user’s device. He compares Chrome’s built-in Gemini Nano with portable WebLLM deployments, then surveys WebGPU and the emerging Web Neural Network API as routes to GPUs and dedicated neural processors.

A Fully Offline AI Web App in Action

The completed architecture transforms the browser’s disconnected dinosaur page into a pre-cached recipe application that keeps working without the user noticing the outage. Jensz demonstrates Chrome’s service-worker and cache-storage tooling, runs a small WebLLM model offline, and applies a Starry Night-style image transformation entirely in the browser.

The Policy Barrier to an Open App Platform

Jensz closes by arguing that platform gatekeepers and their app-store economics—not merely technical limitations—are holding PWAs back. He points to Open Web Advocacy and legislation as ways to promote browser competition, improve implementation quality, and address privacy, security, performance, and user expectations across the open web.

Who's been offline or on spotty connection in the past month? Decent amount of people, less than I think. So I went camping a while back and I was sometimes offline, sometimes without any connection. And you don't normally expect the web to work. And let's say I wanted to cook some pancakes, and just imagine there's no recipe on the back of the tin, and I want to look up a recipe.

Well, you'd probably download a PDF for this. We don't expect the web to work offline, so we expect a PDF to work online. We just download a file or download a program. This is the web's answer when we're offline. We just get a dino game. This is terrible. What if I want to view a recipe website when I've lost connection or when I'm on a spotty connection?

So we want to be able to view our recipe offline. So one method we can get things to work offline is with just a cache control header. So we'll just set a cache control header on our pages. And that tells the browser, hey, you can keep using this resource even when you go offline. And we can store all our assets that we'll need.

And you can set a max age, how long it'll last, and that's in seconds, so that'll be one day. So this is what things will look like online. Obviously, you'll just get your recipe site. If you lost internet momentarily, of course, the page is stored in RAM, so it'll still show. But most browsers, Chromium, sort of give you this no internet connection banner. So you don't need to display anything yourself to tell the user they're offline, because the browser already does that for you.

You can actually go back into the browser, even if you've closed it out of RAM and just opening at cold start, and generally it'll reopen pages that you've already loaded. It'll have a cache stored to disk. So if you come back to a tab that you had open, but you closed the browser, on Chromium, on Android at least, you'll get this little icon that tells you, hey, you're loading a cached resource. And it'll give you some details of when it's acquired.

So this is all just through that caching header. But if you're completely offline and you've closed the tab, and then somehow you remember the URL and come back, you're going to get a dinosaur, even with that caching header. And also, the cache is fairly unreliable.

If you're full on storage, obviously, browser cache is going to be the first thing to go. If you hard reload the page, it'll wipe the cache, no way to get it back. The browser might evict it for various reasons. So maybe other apps want to use storage or the browser might decide, hey, this is super old.

Safari, after seven days, will just wipe everything. And in Incognito, obviously, even with these features we'll talk about, there's no caching. As soon as you close it, you lose everything. So, yeah, we're on spotty connections all the time. I was without Wi-Fi on the flight here. You might be on a cruise ship, camping. So we have to design around this.

The web can't fail as soon as the network fails. And we might also want to store things ahead of time. So let's say we want to add saved recipes and recommendations ahead of time. So a user loads our website and we can just preload things so that when they go offline, they can keep using the site without even noticing that they've lost internet.

And we can do this with the service worker. So the service worker is like a tiny bit of your server that you can actually put in the front end. It's kind of sitting within Chrome. So that handles things like routing, you can intercept any fetch request going through the network, you can interface with the cache, so there's another case we're going to talk about that isn't just a built-in default browser cache, and you can update and version control it just like a native app.

The lifecycle is pretty complex, so you'll register your service worker just from your page or check for updates if you've already got one. Then you'll install it, load any assets that it might need, then it'll be installed, and then you actually have to wait for all the tabs of your site to be closed and for the user to come back to your site in order to activate a new Service Worker, and then it will be activated and controlling web pages.

So all we need is this one line in our script on our page, and we can register any Service Worker file. Where that file lives is what scope the Service Worker has control over. And it's got a few events. So on install, we want to catch all the assets that we need offline. We activate and we can clear off old caches by name, and then we can intercept any fetch request.

So we can say, hey, don't bother going to the network, I actually have this offline. So you can control many clients with one service worker. Any that are open currently, you have to claim clients if you want to take control when there's a new version with clients.claim. And we interface with the cache API in order to serve assets offline. So you can cache all the assets that you might cache with the default browser cache, and you can also cache AI.

So on on our install event, we'll call the event.waitUntil. So this will execute everything inside before exiting, otherwise it might quit, the browser is pretty fussy, even with waitUntil. If it runs too long, the browser is gonna kill it. But we'll open our cache, we'll give it a name, version one, you'll tie this into your build systems, you can have all this versioning, and then we'll add all the resources.

So this will just be adding the the index.html. And then we can intercept fetch requests. So this will be an offline only solution where we're getting the cached asset from the cache. Now, we don't actually reference the cache name there because you can just do caches.match and it will just find anything in any cache.

It's more performant, but you can go by the cache name and then we'll just respond with that resource. You will need a secure connection for this, because these are some pretty powerful features. If hackers got access to your website for all of eternity after the first visit, that wouldn't be good. So you do need a secure connection to do all this.

Now, you're probably thinking, if we're getting everything working offline and we're out camping, we want to look for a recipe. We're not going to open the browser and then type out a URL to get to a recipe website. We want a home icon. We want to compete with native. All you need for that is just a manifest.json file.

And you can define a short name, a full name, a bunch of icons, the start URL where you start when you open the app. Display standalone will mean that instead of opening a browser tab, it'll just open in its own essentially app, its own web frame. And so you can do the usual Android swipe between apps and all sorts of nice features.

You could get it to display just in a tab, up to you. And then you've got a bunch of theme colors and everything. My email app actually does this. So I use this every day. And it's just a web app on my phone, and it feels just like native. Web apps can load faster than native because you often have your browser in RAM. So the entire browser is already sort of stored there, ready to go.

So opening a web app is just that thin web app on top of the browser. But what if we want to update information? We don't just want an offline-only strategy. So one way of doing this is to check if there's internet. And just load from internet if we have a connection. So we can check is navigator.online.

So this is an online first strategy where we check if we have internet. If we do, fetch the resource, just go to the web, go to the internet. If not, then find our resource in the cache, otherwise return 404. So that's a online first strategy. Issue with that is navigator.online is false if we have no connection because obviously we're not connected to anything, we know we're offline.

But if it's true, we have not no connection. So does that mean we have internet? Well, I'm not sure. Okay, we're going through all the routers and systems and maybe connecting to a cell tower.

Do we have internet? Waiting. Oh, we don't have internet. Oh, loved waiting to find out. So, Jake Archibald calls this Li-Fi, which I just love. And he demonstrated by plugging a router into just some soil and, oh, you're technically connected.

And I think this is getting worse, not better. So we're building more and more cell towers, but they're 5G, and these new standards, sure, you get a lot of bandwidth. You're throwing a bucket full of bandwidth, but there's a leaf over there, and your signal won't get through the leaf. So I think it's getting worse with 5G than we had on 4G. So we've got a design around this.

And you might be wondering, okay, I'm only building a business app that's only used by a few important business people, and they're on fiber fast connections. Why do I care about offline first in that case? This is a great article I read recently where it discusses the first TCP packets to your website.

And how important they are. So TCP has this thing called slow start where you only send, I think it's 10 or so packets, or you get 10 packets at the start of a connection, and then each time a packet gets sent successfully, it'll double. And so you get this exponential increase in speed, which is interesting because with internet, sure you've got the download speed, but there's also the latency aspect as well, and latency has a huge impact on the web. But what if we could just avoid this entire issue and just have cached assets?

This is the beauty of offline-first. So this code here will get the cached response from our cache, and then if we have that, just return that, and then we'll try going to the network. So we'll get our network response from our fetch, and then if that's okay, we'll put it in the cache.

So this is if we don't have it in the cache already, we'll go down past there and add it to the cache. We'll have to clone things, because we're going to return one clone of the response, and we're going to put the other in the cache. And then we'll return that network response, and then if there's an error, will create an error.

So that's offline first. If we've got the cached resource, just give people that. Of course, you'll need to add some complexity if you want to update things and version manage assets. There's a lot of complexity that you can go into, but this is how simple it could be. Workbox does a lot of these strategies for you, adds lots of build complexity, lots of things, but Really good tool to add it to your real-life application.

Now, you're probably wondering about browser support. They seem like pretty powerful features. So, should I be bothered? Will people have this feature in their browser? Yes. So, I'm not sure this used to be 97%, but it's gone back down to 94. I think maybe can I use updated some stats?

But pretty much every user has this unless they're on an old iPhone or Internet Explorer, which, you know, that's their fault. It's about as old as Fetch. So if you're using Fetch, you can use Service Workers. Then you're probably wondering, okay, browser support's pretty good. What about how much can I actually store in the browser?

And will it last? Because the Web is Famously, quite ephemeral. Like, if I install an app to the home screen, Safari was killing things after seven days. What happens there? Well, the seven days only applies to non-installed web apps. But what if they run out of storage? How do I manage? How do I know how much I can store even?

A very quick summary is that Chrome basically is like a giant container ship. Store whatever you want. Don't really worry about it. I checked on my PC, which has like two terabyte SSD or whatever, and I had over a terabyte that I could use on one single website. I didn't check whether it warns you before it keeps using that whole storage.

Hopefully it warns you. But I like to think of Firefox and Safari more as like a truck. So you can load tons of stuff, but not a huge amount. And sensible enough to do a lot of pre-caching. But you'd want to use the storage manager API, which gives you estimates on how much is actually available and when you're hitting the limits there and all sorts of other things and pretty decent browser support.

You're probably wondering, how do I decide how much to cache? It's a bit of an ethical question, really. People might be on mobile data, which costs money, and in some countries can cost a lot of money. There's the environment and server costs, so you're paying. With a CDN, it's not as bad, but you're paying for this data egress, and the environment pays as well.

Someone might be on a slow connection, though, so maybe it's better to load things sooner and pre-cachE so that it feels less slow. But maybe then someone else is using their Wi-Fi and that slows down them. Maybe you download all these things and it's unused. And if you check what speed they're on and whether they have prefers reduced data, that's fingerprinting and so that's a privacy consideration.

So there's a lot of tough ethics there and some interesting discussions hopefully at the end of the event, we'll have some good discussions on that. You can also do an offline database. So what if going back to this recipe analogy, I want to have a recipe comment section and I have maybe a BFF that handles all the comments and I want to interface with that.

And I want to index maybe based on most recent, or most upvoted, I can have an offline database. I could essentially clone the BFF and whatever microservices into the web browser. And we can do that with IndexDB, which has really good browser support. IndexDB is kind of known for being horrible to work with, but here's IDB, a small library, that's the entire thing right there that sort of covers up some of the terrible stuff.

You could also use the Origin Private File System. So if you don't want to deal with IDB, you could just get direct access to a hidden file on someone's computer, and then you can use a Read/Write API, which has worse support, to directly access that file. And you could run Wasm SQLite on that.

So you can have an entire SQLite database in the browser, and it's only a few hundred meg, I think. But we've got all this data on the front end. We've got, you know, maybe gigabytes of data. How do we actually sync it with what's happening on a server? How do we update it? And does the app have to be open?

Like, we're trying to compete with native here, how can we actually sync data in the background? What if we want to write out a comment and then click send and then close the app? We don't want to watch it through. Or what if we want to download recipes every morning? If a user's using our site frequently, we might want to just download recipes even when they're not in the app so that when they're on the train on their spotty connection, they can just go through and view them.

Background Sync allows us to do this. Note that you will have to show a notification when you're downloading something, just to show the user that something's happening in the background. So I would argue this is better than Native, because on Native we install an app and we just have to trust that app not to do bad things and not to access all these features without us knowing. Whereas on the web we trust it a little bit less and so it's more private, more secure.

We know what's happening in the background because we've got that notification there. You'll notice somebody is sort of lagging behind. We'll get to that. So notifications as well. You'll probably want to compete with native using notifications. Safari has recently got their own version of them.

Some people, on one side of the fence, people like, we want all these PWA progressive web app features on browsers, and they'll say, Apple's lagging behind massively with iOS. Other side of the fence, people say, Chrome's rushing things. They're adding them before they're fully fleshed out in the spec.

You'll see the previous one, Firefox, didn't have support for background sync either. But I think we should be advocating for the web. I think that we should try and compete with native and have fair browser competition and more PWA features at our disposal. But I do think that we should also discuss features more before we add them to browsers, because we have to support them forever.

Lots of considerations there. But the beauty of the web is that we have one app that works everywhere. We don't have to build multiple code bases. We can just install a PWA on desktop, any mobile, and any device that supports the web, maybe on your smart fridge, recipe app, smart fridge. And we don't have to get gatekeepers permission to publish an update. We can just push a new service worker.

We don't need to be in the app stores, although people do expect apps to sort of be in the app stores. So on Android, I think you can put PWAs in the app store, but on iOS, you can't. So there's some issues there. But we have zero load time. So we're definitely competing with native, even if we don't have support for a lot of these features in one browser.

We do in the other. So it can be a progressive enhancement. I can use my Android PWA, for example, and get instant loading pages, and then maybe I'll use some ancient browser like Internet Explorer. Maybe you support it, and it will just be slower. So you can use these things and not use them at the same time. But AI, wasn't AI in the title?

We've got to add AI to everything. So why might we want to do AI offline? We don't need any API keys. They're expensive. We want to just get the user's device to do all the work. And we don't want to deal with data leaks. We just want everything local first on the user's device. We just want a static CDN that serves them everything, and they can run it.

So one solution for AI in the browser is Google's Gemini Nano, which is built into Chrome. Surprisingly, it doesn't support Android or Chrome OS, but it supports desktop. It even has Linux support. So you're going to need 22 gigs of space, an unmeted connection, four gigs of RAM, and it's pretty simple.

You just do languagemodel.create. I haven't played around with it too much, but it's quite good because it ships Gemini with the browser, and then it shares it among origins. So if one website loads it, you have access to it, which we kind of had a fingerprinting issue with fonts in that regard, but with AI, it's so much heavier, and maybe we'll change this in the future, but there is that privacy consideration.

Interesting trade-offs there. But it's really good for just if you want to add a sprinkle of AI for Chrome users. You'll get a loading hook that you can tie into to display to the user if you're loading Gemini for the first time. So if a previous site's already loaded it, you won't need to load it. And you can also set the temperature among all sorts of other things. But not everyone uses Chrome.

We want to look into something that everyone can use. Web LLM is one of many libraries that can run on top of CPU, GPU, or WebNN potentially. Web GPU is pretty good in terms of browser support. It's actually landed in Safari, it's in Firefox.

Once people update their browsers, this is coming really soon, it replaces WebGL a little bit. But it lets us run AI really efficiently. But what about NPUs? Everyone's getting an AI chip in their phone. If we want to compete with native, we have to tie into this on the web. So there's this new web neural network API, and there's a lot of work happening here, rapidly moving.

And this essentially gives you low-level access. It's like a primitive on the web for AI. It's really good bedtime reading. I'm not going to summarize too much on that. I want you to go and dig in and make comments and have your own opinion on what they're discussing. There's a Discord as well. So the browser support, it's behind a flag in Chrome, so you can play around with it, but definitely not ready for production.

So we want to play around with WebGPU a bit more. But you can turn it on through flags. And it's just those two there, and that'll let you play around with it in Chrome. It can do some cool things, like it can make an image starry night-ified, if that makes any sense.

But essentially, I didn't get a chance to finish a demo, but if we started off with just a dinosaur, no internet page on the web, We've now ended up with a full-fledged recipe website. We can load everything offline. We've pre-cached favorited pages or saved pages and anything that we might think the user wants to visit.

So we've gone from dyno to full-fledged website and the user doesn't even know that they're offline. So service workers, there's a lot of nice dev tools. I might zoom in a bit more. So you can just go to this application tab in Chrome. I've got a service worker.

This is a meetup that I go to quite a bit in Melbourne. So I made this a while back. If I turn off internet, this is a bit risky. And I reload. Please work. Yeah, it doesn't even show it reloading. So it's actually is reloading. And in here, you can see the size is empty because we're just loading from a service worker.

If I can expand that at all. You can see here, service worker. So we're loading all of this offline. So you can go into service workers, you can view the entire source code for your worker, you can go into the cache storage. So this is different from the built-in browser cache. This is the cache that we're controlling.

Programmatically, and we get much more control over everything here. We can catch any asset we need, including AI. So if I go here, this is an AI built with web LLM, which runs on top of web GPU. So it's not very intelligent, this one, but let's ask it a question. We might get a cursed answer here.

Let's find out. Oh, this is terrible. So you might want to load in a smarter one. This one's very tiny. But yeah, you can run AI in the web. It's not really selling it, is it? Let's do something cooler. Let's starry nightify something. So we'll set the GPU backing.

Of course, in the future, this can go straight onto your AI chips. We'll pick an image, go through all my private GIFs. Mr. Bean. There's Mr. Bean's Starry Nightified. So that's running all in the browser. You can see I've got no internet connection at all. So why aren't we building PWAs?

Why are there no web apps like this out? I think there's one culprit. One main culprit, of course, both duopolies don't want a free and open platform to succeed. One of them might push the web more than the other, but neither of them want to give up their 30% cut.

There's open web advocacy, which John has had a speaker on before, talking about how to teach legislators web technologies and push them to advocate on our behalf for opening the web up and supporting fair browser competition. Legislation is the main issue stopping all these technologies from proliferating, but that ties into if we're not forcing certain companies to play fair, we're getting bugs, user expectations are low, privacy is used as a scapegoat for a lot of things, There are some privacy considerations. Security, it might be less secure because there's so many more features on the web, but if we use the web more, we'll have more resources to fix those.

Performance is another issue, but all of these tie into legislation. So if we support more fair competition, then we'll get a much better web for everyone. Thanks for listening. That's my newsletter. I'll publish updates on all this stuff and you can find my LinkedIn. All opinions in this talk are my own and I'm also doing Movember, so if you want to support the cause, come and speak to me after.

But yeah, I think that's everything. Thank you.

The Service Worker

Fireworks illustrate the introduction of service workers as the key technology for offline web applications.

Service worker

A service worker sits between browser clients and the server, intercepting requests and passing responses back to the clients.

Service worker

  • Routing
  • Fetching
  • Caching
  • Updates and version control

A central service worker coordinates four responsibilities: routing, fetching, caching, and updates with version control.

Service worker lifecycle

Current session

Register or check for updates → Install → Installed

Next session

Activating → Activated

A lifecycle diagram shows registration or an update check leading to installation. The installed worker waits, then activates and becomes active in a subsequent session.

navigator.serviceWorker.register("sw.js")
self.addEventListener("install", (event) => {
  // Cache assets for offline
});
self.addEventListener("activate", (event) => {
  // Delete old caches & clean up
});
self.addEventListener("fetch", (event) => {
  // Handle all network requests
});

One service worker, many clients

A single service worker is surrounded by multiple browser clients, showing that one worker can control many open pages.

clients.claim()

Cache API

A squirrel stockpiling acorns is paired with web assets—including JavaScript, HTML, CSS, fonts, images, and AI—to represent resources stored through the Cache API.

self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open("v1").then((cache) => {
      cache.addAll([
        "/"
      ])
    })
  )
})
self.addEventListener("fetch", async (event) => {
  const cachedAsset = await caches.match(event.request.url)
  event.respondWith(cachedAsset)
});

HTTPS required

A security guard beside an HTTPS lock symbolizes that service workers require a secure connection.

Home icons

manifest.json

{
  "short_name": "Zach's Recipes",
  "name": "Zach's Recipes Web App",
  "icons": [
    {
      "src": "/favicon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/favicon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "red",
  "background_color": "black"
}

Fastmail

An Android home screen shows Fastmail installed with its own icon, illustrating a web app that launches and feels like a native application.

Updates

Mouldy bread represents cached content becoming stale and needing updates.

if (navigator.onLine)

A contemplative Batman underscores uncertainty about whether the browser's online status proves that the internet is reachable.

self.addEventListener("fetch", (event) => {
  event.respondWith(
    navigator.onLine
      ? fetch(event.request.url)
      : caches.match(event.request.url) || new Response('Not found', { status: 404 })
  );
});
if (navigator.onLine)

false = no connection

A closed road and a person stranded without service illustrate that a false status reliably indicates no network connection.

if (navigator.onLine)

true = not no connection

A long wait and a closed business illustrate that being connected to a network does not guarantee usable internet access.

Internet?

A child from the “Is this real life?” meme conveys uncertainty about whether network connectivity means internet availability.

No internet

ERR_INTERNET_DISCONNECTED

A browser error page confirms that the attempted connection ultimately has no internet access.

Lie-Fi

A Wi-Fi signal labelled “Lie-Fi” is paired with Pinocchio, representing a network connection that misleadingly claims connectivity without providing internet access.

A router connected to soil

A conference demonstration shows a router plugged into soil: the device may report a connection even though the connection cannot reach the internet.

4G and 5G

4G is represented by a pressure washer, while 5G is represented by several small spray bottles, contrasting reach and resilience with greater but more easily obstructed capacity.

Fast connections

Fibre-optic cables surround a driver experiencing extreme acceleration, illustrating the assumption that fast connections eliminate performance concerns.

Why your website should be under 14kB in size

A 14kB page can load much faster than a 15kB page—possibly 612ms faster—because of TCP slow start.

A screenshot of an article highlights the disproportionate loading advantage of fitting a page into the initial TCP transfer window.

Slow start vs Congestion Avoidance

A graph plots the TCP congestion window against time. Each cycle begins with exponential slow start, changes to linear congestion avoidance, then drops sharply when congestion occurs; the slow-start threshold decreases after each event.

Offline first

async function cacheFirst(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) return cachedResponse;
  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      const cache = await caches.open("bob");
      cache.put(request, networkResponse.clone());
    }
    return networkResponse;
  } catch (error) {
    return Response.error();
  }
}

Workbox

Production-ready service worker libraries and tooling.

The Workbox logo accompanies the project summary.

Browser support

Service Workers

Baseline: Widely available across major browsers.

Global usage: 94.46%

A browser-compatibility chart shows broad service-worker support across major desktop and mobile browsers.

Service workers are about as old as fetch()

A humorous collage pairs a dog labelled fetch() with a surprised computer user and a large number nine, emphasizing the maturity of both browser features.

Storage limits & persistence

Browser storage capacity

Chrome, Firefox, and Safari logos are compared using freight metaphors: Chrome is represented by a huge container ship, while Firefox and Safari are represented by a smaller moving truck.

Storage manager

Baseline 2023: Newly available across major browsers.

navigator.storage reports the availability and persistence of site data stored through APIs such as Cache and IndexedDB.

Usage: Australia 88.84%; global 92.97%.

A compatibility chart indicates broad support across current desktop and mobile browsers, with some partial support in older Safari versions.

Preloading ethics

  • Mobile data costs
  • Slow connections
  • Environment & server costs
  • Fingerprinting
  • Unused data

Offline Database

  • Recipe comments
  • Indexing

IndexedDB 2.0

Improvements include getAll(), renaming stores and indexes, and binary keys.

Global usage: 97.5%.

A browser-compatibility chart shows strong IndexedDB 2.0 support across major desktop and mobile browsers.

idb

A small library that provides a more convenient interface to IndexedDB.

The complete minified browser bundle is shown, illustrating the library's small size.

Origin private file system

navigator.storage.getDirectory() returns an origin-restricted directory handle, invisible to the user's normal file system, for file-based applications such as SQLite databases.

Global usage: 92.14%.

A compatibility chart shows broad support in current browsers, with partial support in some older versions.

SQLite in the browser

SQLite 3 can run through WebAssembly using the origin private file system.

A browser demo reports that SQLite initialized successfully, OPFS is available, a database and table were created, data was inserted, and three rows were queried.

Background sync?

  • Send comments offline
  • Download recipes each morning

Background sync

The Background Synchronization API registers a service-worker callback that runs when the device is online again, allowing an app to work offline and synchronize after reconnection.

Availability: Limited across major browsers.

Usage: Australia 65.94%; global 82.21%.

A compatibility chart shows full support in Chromium-based browsers but limited or absent support in several others.

Notifications

Web Notifications

Baseline: Widely available across major browsers.

Alerts users outside a web page by displaying notifications that do not require user interaction.

Global usage: 92.05%.

A compatibility chart shows broad support, including partial support in Safari on iOS.

Background sync

The Background Synchronization API registers a service-worker callback that runs when the device is online again.

Availability: Limited across major browsers.

The compatibility chart highlights uneven browser support, particularly outside Chromium-based browsers.

Web Notifications

Baseline: Widely available across major browsers.

Alerts users outside a web page by displaying notifications that do not require user interaction.

The compatibility chart shows that notifications are broadly supported, though implementation details vary among browsers.

One App, works everywhere

A Toy Story image labels Woody as “Native apps” and Buzz Lightyear as “Web apps,” with Buzz gesturing toward a web, presenting the web as a cross-platform application target.

Zero Gatekeepers

A humorous image associates Android and iOS app stores with a controlling gatekeeper and a formation of guards, contrasting app-store approval with direct web publishing.

Zero Load Time

A rocket launch and a startled high-speed driver illustrate the near-instant startup possible when a web app's resources are already cached.

But AI

API Keys

A golden key is placed over a person lying on a large pile of money, illustrating the expense associated with hosted AI API access.

Data Leaks

Two leak-related reaction images humorously represent the privacy risks of sending user data to remote AI services.

Gemini Nano in-browser Prompt API

Supported OS

  • Windows 10+
  • macOS 13+
  • Linux

Unsupported OS

  • Android
  • iOS
  • ChromeOS

Requirements

  • At least 22 GB free storage
  • Unmetered connection
  • 4 GB RAM

Prompt API documentation

The sequence progressively reveals Gemini Nano's desktop-only operating-system support and its substantial local requirements: free disk space, an unmetered download connection, and sufficient memory.

const session = await LanguageModel.create({
  initialPrompts: [
    {
      role: 'system',
      content:
        'You are a skilled analyst who correlates patterns across multiple images.',
    },
  ],
  expectedInputs: [{ type: 'image' }],
});

fileUpload.onchange = async () => {
  await session.append([
    {
      role: 'user',
      content: [
        {
          type: 'text',
          value: `Here's one image. Notes: ${fileNotesInput.value}`,
        },
        { type: 'image', value: fileUpload.files[0] },
      ],
    },
  ]);
};

analyzeButton.onclick = async (e) => {
  analysisResult.textContent = await session.prompt(userQuestionInput.value);
};

Model loading and temperature

The Prompt API exposes loading progress for a model's first download and supports generation controls such as temperature.

The final build overlays a slow loading indicator and a thermometer on the Prompt API example, emphasizing that local model initialization may take time and that response randomness can be configured.

Not everyone uses Chrome

@mlc-ai/web-llm

A modular JavaScript package that runs language-model chats directly in web browsers with hardware acceleration, no server support, and WebGPU.

A package-page screenshot introduces WebLLM as a browser-based alternative for running local language models beyond Chrome's built-in API.

WebGPU

Limited availability across major browsers

An API for complex rendering and compute using hardware acceleration, intended to supersede WebGL.

Usage: Australia 67.24%; Global 79.53%

A browser-compatibility chart shows broad WebGPU support in current Chrome and Chrome for Android, with partial or version-dependent support across other major desktop and mobile browsers.

NPUs

Web Neural Network API

W3C Candidate Recommendation Draft, 15 September 2025

A screenshot of the W3C specification shows a table of contents dominated by machine-learning use cases, including person detection, style transfer, speech recognition, text generation, and video summarization.

ML API

Usage: Australia 0%; Global 0%

A browser-compatibility chart shows the machine-learning API as unsupported by default across major desktop and mobile browsers, with experimental implementations indicated for some versions.

chrome://flags/

A surprised cat holds a Union Jack, introducing the need to enable experimental WebNN features through Chrome flags.

WebNN flags

  • Enables WebNN API
  • Enables experimental WebNN API features
A Chrome flags screenshot shows both the WebNN API and its additional experimental features set to Enabled.

WebNN samples

  • Handwritten Digits Classification
  • Fast Style Transfer
  • Noise Suppression
  • Object Detection
  • Semantic Segmentation
A gallery presents practical browser-based neural-network demonstrations using images, audio waveforms, handwritten digits, and detected or segmented subjects.

From offline fallback to a complete offline app

The basic Chrome offline dinosaur page is contrasted with a polished pancake-recipe page, illustrating the progression from a minimal failure screen to useful content that remains available without a network connection.

Offline web apps and in-browser AI

Service workers and controlled cache storage can keep an application and its assets available offline, including assets needed for local AI.

The demonstration inspects a MelbJS site's active service worker and cache, reloads the site without transferring its cached assets from the network, and confirms that requests are served by the service worker. It then opens a WebLLM chat that loads a small language model through WebGPU and moves to a WebNN fast-style-transfer sample, demonstrating that browser AI workloads can be prepared to run locally.

Underrated

An aerial photograph of Apple Park shows its large ring-shaped headquarters surrounded by suburban development.

Fast Style Transfer

Backend: WebNN (GPU)

Inference time: approximately 13 ms

The grouped demonstration selects an animated image of Mr Bean, applies the visual character of The Starry Night using WebNN on the GPU, and displays the transformed portrait beside the original. A final view of the disabled Wi-Fi control verifies that the inference and result remain available without an internet connection.

Underrated

An aerial photograph of Apple Park shows its large ring-shaped headquarters, representing Apple in the discussion of platform control and the open web.

Open Web Advocacy

We are a group of software engineers from all over the world who have come together to advocate for the future of the open web.

A screenshot of the Open Web Advocacy website highlights its work explaining technical and anti-competitive issues to regulators, legislators, and policy makers.

Legislation

  • Bugs
  • User expectations
  • Privacy
  • Security
  • Performance

newsletter.zach.net

A QR code provides quick access to Zach Jensz’s newsletter.

People

  • Jake Archibald

Technologies & Tools

  • Chromium
  • Service Worker
  • Cache API
  • Workbox
  • IDB
  • SQLite
  • WebLLM

Standards & Specs

  • Cache-Control
  • clients.claim
  • event.waitUntil
  • Web App Manifest
  • navigator.onLine
  • 5G
  • StorageManager API
  • IndexedDB
  • Origin Private File System
  • WebAssembly
  • Background Sync
  • Language Model API
  • WebNN
  • WebGPU
  • WebGL

Concepts & Methods

  • Progressive Web App
  • Online-first
  • Li-Fi
  • TCP slow start
  • Offline-first
  • Progressive enhancement
  • Local-first

Organisations & Products

  • Gemini Nano
  • Open Web Advocacy