Building a Browser-Based AI Feature With No Backend
Oct 27, 2026 For a long time, adding AI to a web app meant one thing: sending user data to a server, waiting for a hosted model to answer, and paying for every request. That is no longer the only option. Modern browsers can run compact machine learning models directly on a visitor's device, using W

For most developers starting out, Transformers.js offers the gentlest learning curve because a few lines of code give you a working model. Building a Small Example Let's build a simple feature: classifying the sentiment of a user's text entirely in the browser. The pattern applies to many other tasks.
Step 1: Detect What the Device Can Do Check for WebGPU first, and fall back to WASM when it isn't available: async function pickDevice() { if ('gpu' in navigator) { try { const adapter = await navigator.gpu.requestAdapter(); if (adapter) return 'webgpu'; } catch (err) { console.warn('WebGPU check failed', err); } } return 'wasm'; } Step 2: Load the Model With Transformers.js, loading a model is a single call. The library downloads the files once and caches them for later visits: import { pipeline } from '@huggingface/transformers';
const classify = await pipeline( 'sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english', { device } ); Step 3: Run Inference const result = await classify('The new update is fantastic.'); console.log(result); // [{ label: 'POSITIVE', score: 0.99 }] That's the whole core of the feature. Everything else is about making it feel good to use. Step 4: Keep the Interface Responsive Inference is heavy, and running it on the main thread can freeze your page. Move it into a Web Worker and talk to it with messages: // worker.js import { pipeline } from '@huggingface/transformers';
self.onmessage = async (event) => { const { type, text, device } = event.data;
if (type === 'load') { classifier = await pipeline( 'sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english', { device } ); self.postMessage({ type: 'ready' }); }
if (type === 'run' && classifier) { const output = await classifier(text); self.postMessage({ type: 'result', output }); } }; // main.js const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.postMessage({ type: 'load', device: await pickDevice() });