AI · AI for developers
How do you run an AI model in the browser?
Short answer
You download a model file, usually in ONNX format, and run it inside the web page with a runtime such as ONNX Runtime Web, often through Transformers.js. The runtime uses the GPU through WebGPU when available and falls back to the CPU with WebAssembly. Files are cached after the first visit, and your data never leaves the device.
Open Remove backgroundWhy run AI in the browser at all
Most AI features call a server: the photo or text is uploaded, a GPU in a data center runs the model, and the result comes back. In-browser AI turns this around. The model is downloaded once, and the visitor's own device does the work.
- Privacy: photos, audio and documents stay on the device. Nothing needs to be uploaded or stored, which also simplifies compliance with privacy laws in many countries.
- No server cost per use: the site pays to host the model files, not for GPU time. This makes free tools possible.
- Works offline after the first load, and has no network delay per request.
- No account or API key is needed.
The trade-offs are real. The first visit needs a large download, speed depends on the visitor's hardware, and only small and medium models fit. Large chat models with tens of billions of parameters still need a server. Tasks that fit well today include background removal, image upscaling, depth estimation, speech to text, translation, text classification, embeddings and small image or language models. For example, cv.cm's remove-background and speech-to-text tools run BiRefNet and Whisper this way, with nothing uploaded.
The four layers: model, runtime, backend, library
| Layer | What it is | Common choice |
|---|---|---|
| Model file | The trained weights and the compute graph | ONNX files, often exported from PyTorch |
| Runtime | Code that executes the graph in the page | ONNX Runtime Web (alternatives exist, such as TensorFlow.js or WebLLM for chat models) |
| Backend | The hardware path the runtime uses | WebGPU for the GPU, WebAssembly (WASM) for the CPU, WebNN where supported |
| Library | Friendly API: preprocessing, tokenizers, pipelines | Transformers.js, which uses ONNX Runtime underneath |
ONNX is an open format for neural networks. A model trained in PyTorch can be exported to ONNX, and ONNX Runtime Web can then run it with several backends. The WASM backend supports every ONNX operator. The GPU backends support a subset, so a model can fail on WebGPU and still work on WASM. Transformers.js adds what you would otherwise write yourself: resizing images, turning text into tokens, decoding audio, and turning raw output numbers into labels, masks or text.
WebGPU versus WebAssembly
WebGPU is a W3C web standard that gives pages access to the graphics card for both drawing and general computation. For neural networks, the compute part is what matters: matrix multiplications run on many GPU cores in parallel. WebAssembly runs compiled code on the CPU at close to native speed and works in every modern browser.
| Question | WebGPU | WebAssembly (WASM) |
|---|---|---|
| Runs on | GPU | CPU, optionally multi-threaded with SIMD |
| Speed on large models | Often many times faster | Slower, but predictable |
| Availability | Growing, but not in every browser and device; MDN lists it as limited availability | Every modern browser |
| Operator coverage in ONNX Runtime Web | A subset | All operators |
| Requirements | Secure context (HTTPS) | Threads need a cross-origin isolated page |
| Best for | Image generation, large vision models, Whisper on long audio | Small models, fallback, devices without a good GPU |
In practice, try WebGPU first and fall back to WASM. Do not decide by browser name. Ask the browser for an adapter and check its features:
// Check what this device can do before loading a model.
async function pickBackend() {
if (!("gpu" in navigator)) return { device: "wasm" }; // no WebGPU API
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) return { device: "wasm" }; // API exists, no usable GPU
const f16 = adapter.features.has("shader-f16"); // half-precision support
return { device: "webgpu", f16 };
}
console.log(await pickBackend(), "threads:", self.crossOriginIsolated);Multi-threaded WASM uses SharedArrayBuffer, which browsers only allow when the page is cross-origin isolated. That means sending the Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers. Without them, WASM runs on a single thread and is much slower.
A minimal Transformers.js example
With Transformers.js, a task is one pipeline call. The library downloads the model, picks the files for the chosen device and data type, and returns a function you can call:
import { pipeline } from "@huggingface/transformers";
const { device } = await pickBackend(); // from the snippet above
// First call downloads the model and caches it. Later visits load from cache.
const classify = await pipeline(
"sentiment-analysis",
"Xenova/distilbert-base-uncased-finetuned-sst-2-english",
{
device, // "webgpu" or "wasm"
dtype: device === "webgpu" ? "fp32" : "q8",
progress_callback: (p) => console.log(p.status, p.file ?? "", p.progress ?? ""),
}
);
console.log(await classify("This runs entirely on my laptop."));
// [{ label: "POSITIVE", score: 0.99... }]Run this in a module script or, better, in a Web Worker. Model inference can block for hundreds of milliseconds or more, and in a worker the page stays responsive. WebGPU is also available inside workers. Show download progress to the user, because the first load of a large model can take a while on a slow connection.
Download size, quantization and caching
The download is roughly the number of parameters times the bytes stored for each one. This simple arithmetic tells you whether a model is practical:
download size ~ number of parameters x bytes per parameter
fp32 (4 B) fp16 (2 B) int8 / q8 (1 B) q4 (0.5 B)
50 million params 200 MB 100 MB 50 MB 25 MB
500 million params 2.0 GB 1.0 GB 500 MB 250 MB
1 billion params 4.0 GB 2.0 GB 1.0 GB 500 MB
Real files differ a little: some layers stay at higher precision,
and the tokenizer and config add a few MB.Quantization stores weights with fewer bits: fp16 halves the size of fp32, 8-bit quantization (q8) quarters it, and 4-bit (q4) reduces it by about eight times. Smaller weights download faster and need less memory. The cost is some accuracy, which is usually small at 8 bits and more visible at 4 bits, especially on small models and on tasks such as image generation where small errors show. Transformers.js selects the variant with its dtype option. Its defaults are q8 on WASM and fp32 on WebGPU, and the available options depend on what the model publisher exported.
Caching makes the second visit fast. Transformers.js stores downloaded files with the browser's Cache API by default. Browsers give each site a storage quota based on disk size, often large enough for several gigabytes on a desktop, but this storage is best-effort: the browser can delete it when the device is low on space, and Safari can remove script-created data for sites that the user has not interacted with for several days. A site can ask for persistent storage with navigator.storage.persist() and check usage with navigator.storage.estimate().
Phones and other limits
- Memory: a model needs memory for its weights plus working space. Mobile browsers give each tab much less memory than desktops and may close the tab without warning if a model is too large. Keep mobile models small, often under a few hundred megabytes.
- WebGPU on phones: support depends on the browser, the operating system version and the GPU driver. Always have a WASM path or a clear message.
- Heat and battery: long GPU work makes phones hot, and the system then slows the chip down. A task that takes 2 seconds once may take much longer the fifth time.
- Data plans: warn users before a large download on a mobile connection.
- Precision: some GPUs lack fast half-precision (fp16) support or produce slightly different numbers, so outputs can differ a little between devices.
- Model licenses still apply. Running a model in the browser does not change its terms of use.
A checklist for shipping in-browser AI
- Choose the smallest model that meets your quality bar, and test its quantized versions side by side.
- Detect WebGPU with an adapter request, and fall back to WASM.
- Serve the site over HTTPS, and enable cross-origin isolation if you need WASM threads.
- Run inference in a Web Worker and show download and processing progress.
- Do not download the model on page load. Wait until the user starts a task, so visitors who only read the page do not pay for gigabytes.
- Host the model files at a pinned version, and cache them.
- Test on a low-end phone, not only on a development laptop.
FAQ
Is in-browser AI really private?
The model runs on your device, so your input does not need to be sent anywhere. Whether a specific site uploads anything depends on its code, so check its privacy statement or the browser's network panel.
Which browsers support WebGPU?
Support has grown across major desktop browsers and some mobile ones, but it still varies by browser version, operating system and GPU. Check the current compatibility table on MDN and test for navigator.gpu in code.
Can I run a ChatGPT-like model in the browser?
Small language models with up to a few billion parameters can run in the browser on a good GPU, after a download of one gigabyte or more. They are far less capable than the large models behind ChatGPT, Claude or Gemini.
Why is the first run slow and the second fast?
The first run downloads the model and compiles GPU shaders or WASM code. Later runs load the files from the browser cache, as long as the browser has not cleared that storage.
Does running AI in the browser use my GPU even without a gaming card?
Yes. WebGPU can use integrated graphics in ordinary laptops and phones. It is slower than a dedicated card, but often still much faster than the CPU path.