Download AI models with the Background Fetch API  |  web.dev Skip to main content Resources Web Platform Dive into the web platform, at your pace. HTML CSS JavaScript User experience Learn how to build better user experiences. Performance Accessibility Identity Learn Get up to speed on web development. Learn HTML Learn CSS Learn JavaScript Learn AI Learn Performance Learn Accessibility More courses Additional resources Explore content collections, patterns, and more. AI and the web Explore PageSpeed Insights Patterns Podcasts & shows Developer Newsletter About web.dev Discover Baseline How to use Baseline Blog Case Studies / English Deutsch Español – América Latina Français Indonesia Italiano Polski Português – Brasil Tiếng Việt Türkçe Русский עברית العربيّة فارسی हिंदी বাংলা ภาษาไทย 中文 – 简体 中文 – 繁體 日本語 한국어 Sign in Resources AI and the web Fast load times Learn Core Web Vitals Identity Progressive Web Apps Payments Notifications How to optimize INP Network reliability React Animations Mini apps Media Safe and secure WebAssembly Devices Easily discoverable Test automation Angular Resources More AI and the web Fast load times Learn Core Web Vitals Identity Progressive Web Apps Payments Notifications How to optimize INP Network reliability React Animations Mini apps Media Safe and secure WebAssembly Devices Easily discoverable Test automation Angular Discover Baseline How to use Baseline Blog Case Studies Introduction What is AI? Ethics and AI Right-sized AI and sustainability Agents Introduction to agents Agent-friendly sites Practical tips Understand LLM size Compare model capabilities Choose a right-sized model Improve performance and UX Prompt engineering Efficiently download AI models Start building Upgrade your site search Combat toxicity Use client-side AI to combat online toxicity Build client-side AI toxicity detection Improve product review suggestions Built a local and offline-capable chatbot Benefits and limits of large language models Build a chatbot with WebLLM Build a chatbot with the Prompt API Resources Learn AI Watch to learn The foundation model in Chrome Gemini tutorials Web Platform HTML CSS JavaScript User experience Performance Accessibility Identity Learn Learn HTML Learn CSS Learn JavaScript Learn AI Learn Performance Learn Accessibility More courses Additional resources AI and the web Explore PageSpeed Insights Patterns Podcasts & shows Developer Newsletter About web.dev We want to hear from you! We are looking for web developers to participate in user research, product testing, discussion groups and more. Apply now to join our WebDev Insights Community. web.dev Resources Download AI models with the Background Fetch API Stay organized with collections Save and categorize content based on your preferences. Thomas Steiner GitHub LinkedIn Mastodon Bluesky Homepage Published: February 20, 2025 Reliably downloading large AI models is a challenging task. If users lose their internet connection or close your website or web application, they lose partially downloaded model files and have to start over on return to your page. By using the Background Fetch API as a progressive enhancement, you can improve the user experience significantly. Browser Support 74 79 x x Source Note: You can read about more best practices for AI model downloads in Improve performance and UX for client-side AI. Register a service worker The Background Fetch API requires your app to register a service worker. if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { const registration = await navigator.serviceWorker.register('sw.js'); console.log('Service worker registered for scope', registration.scope); }); } Trigger a background fetch As the browser fetches, it displays progress to the user and gives them a method to cancel the download. Once the download is complete, the browser starts the service worker and the application can take action with the response. Tip: Prevent unnecessary and expensive downloads for your users. Wait to load the model until your app needs it or there's enough user engagement to signal they'll stay on the page. The Background Fetch API can even prepare the fetch to start while offline. As soon as the user reconnects, the download begins. If the user goes offline, the process pauses until the user is online again. In the following example, the user clicks a button to download Gemma 2B. Before we fetch, we check if the model was previously downloaded and cached, so we don't use unnecessary resources. If it's not cached, we start the background fetch. const FETCH_ID = 'gemma-2b'; const MODEL_URL = 'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin'; downloadButton.addEventListener('click', async (event) => { // If the model is already downloaded, return it from the cache. const modelAlreadyDownloaded = await caches.match(MODEL_URL); if (modelAlreadyDownloaded) { const modelBlob = await modelAlreadyDownloaded.blob(); // Do something with the model. console.log(modelBlob); return; } // The model still needs to be downloaded. // Feature detection and fallback to classic `fetch()`. if (!('BackgroundFetchManager' in self)) { try { const response = await fetch(MODEL_URL); if (!response.ok || response.status !== 200) { throw new Error(`Download failed ${MODEL_URL}`); } const modelBlob = await response.blob(); // Do something with the model. console.log(modelBlob); return; } catch (err) { console.error(err); } } // The service worker registration. const registration = await navigator.serviceWorker.ready; // Check if there's already a background fetch running for the `FETCH_ID`. let bgFetch = await registration.backgroundFetch.get(FETCH_ID); // If not, start a background fetch. if (!bgFetch) { bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, { title: 'Gemma 2B model', icons: [ { src: 'icon.png', size: '128x128', type: 'image/png', }, ], downloadTotal: await getResourceSize(MODEL_URL), }); } }); The getResourceSize() function returns byte size of the download. You can implement this by making a HEAD request. const getResourceSize = async (url) => { try { const response = await fetch(url, { method: 'HEAD' }); if (response.ok) { return response.headers.get('Content-Length'); } console.error(`HTTP error: ${response.status}`); return 0; } catch (error) { console.error('Error fetching content size:', error); return 0; } }; Report download progress Once the background fetch begins, the browser returns a BackgroundFetchRegistration. You can use this to inform the user of the download progress, with the progress event. bgFetch.addEventListener('progress', (e) => { // There's no download progress yet. if (!bgFetch.downloadTotal) { return; } // Something went wrong. if (bgFetch.failureReason) { console.error(bgFetch.failureReason); } if (bgFetch.result === 'success') { return; } // Update the user about progress. console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`); }); Notify users and client of fetch completion When the background fetch succeeds, your app's service worker receives a backgroundfetchsuccess event. The following code is included in the service worker. The updateUI() call near the end lets you update the browser's interface to notify the user of the successful background fetch. Finally, inform the client about the finished download, for example, using postMessage(). self.addEventListener('backgroundfetchsuccess', (event) => { // Get the background fetch registration. const bgFetch = event.registration; event.waitUntil( (async () => { // Open a cache named 'downloads'. const cache = await caches.open('downloads'); // Go over all records in the background fetch registration. // (In the running example, there's just one record, but this way // the code is future-proof.) const records = await bgFetch.matchAll(); // Wait for the response(s) to be ready, then cache it/them. const promises = records.map(async (record) => { const response = await record.responseReady; await cache.put(record.request, response); }); await Promise.all(promises); // Update the browser UI. event.updateUI({ title: 'Model downloaded' }); // Inform the clients that the model was downloaded. self.clients.matchAll().then((clientList) => { for (const client of clientList) { client.postMessage({ message: 'download-complete', id: bgFetch.id, }); } }); })(), ); }); Receive messages from the service worker To receive the sent success message about the completed download on the client, listen for message events. Once you receive the message from the service worker, you can work with the AI model and store it with the Cache API. navigator.serviceWorker.addEventListener('message', async (event) => { const cache = await caches.open('downloads'); const keys = await cache.keys(); for (const key of keys) { const modelBlob = await cache .match(key) .then((response) => response.blob()); // Do something with the model. console.log(modelBlob); } }); Cancel a background fetch To let the user cancel an ongoing download, use the abort() method of the BackgroundFetchRegistration. const registration = await navigator.serviceWorker.ready; const bgFetch = await registration.backgroundFetch.get(FETCH_ID); if (!bgFetch) { return; } await bgFetch.abort(); Cache the model Cache downloaded models, so your users only download the model once. While the Background Fetch API improves the download experience, you should always aim to use the smallest possible model in client-side AI. Together, these APIs help you create a better client-side AI experience for your users. Demo You can see a complete implementation of this approach in the demo and its source code. With Chrome DevTools, you can preview the events related to the ongoing background fetch. The demo shows an ongoing download that's 17.54M megabytes done, with 1.26 gigabytes in total. The browser's Download indicator also shows the ongoing download. Are you wondering about client-side AI? Tweet us @ChromiumDev, share with us on BlueSky, file a request for content, or set up one-on-one office hours with the Web.dev AI Team. Acknowledgements This guide was reviewed by François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas, and Alexandra Klepper. Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates. Last updated 2025-02-20 UTC. [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-02-20 UTC."],[],[]] web.dev web.dev We want to help you build beautiful, accessible, fast, and secure websites that work cross-browser, and for all of your users. This site is our home for content to help you on that journey, written by members of the Chrome team, and external experts. Contribute File a bug See open issues Related Content Chrome for Developers Chromium updates Case studies Podcasts & shows Follow @ChromiumDev on X YouTube Chrome for Developers on LinkedIn RSS Terms Privacy Manage cookies English Deutsch Español – América Latina Français Indonesia Italiano Polski Português – Brasil Tiếng Việt Türkçe Русский עברית العربيّة فارسی हिंदी বাংলা ภาษาไทย 中文 – 简体 中文 – 繁體 日本語 한국어