Modern Agent Engineering

Browser LLMs + WebMCP + MCP UI: The Coming Frontend Stack

Browser LLMs + WebMCP + MCP UI: The Coming Frontend Stack

Chrome now ships an on-device LLM, WebMCP lets your pages expose tools to agents, and MCP Apps standardizes interactive tool-result UI. Here's how the three fit together, what the benchmarks say, and how to build an inline copilot today.

30 min read

Three separate standardization efforts landed within months of each other, and together they redraw the frontend map:

  1. The browser grew a brain. Chrome 148 stabilized the Prompt API: any website can now prompt an on-device Gemini Nano model with text, image, and audio inputs, structured JSON-schema output, and streaming.
  2. Pages learned to expose hands. WebMCP entered origin trial in Chrome 149, letting a web page register typed tools (checkout, filter_results, submit_application) that browser agents can discover and call instead of blindly clicking through the DOM.
  3. Tool results grew a face. MCP Apps (SEP-1865, Final) standardized how an MCP tool ships an interactive UI resource that renders, sandboxed, inside agent hosts.

Reasoning, actuation, presentation — all three moving into the web platform at once. My take: the frontend app of the next few years is a page that thinks locally, exposes tools declaratively, and renders agent output as structured surfaces instead of chat paragraphs.

Written against the platform as of September 2026. Version-pinned claims say so inline — when in doubt, check Chrome Status before you build.

TL;DR

  • Chrome’s built-in AI is production-shaped: Summarizer, Translator, and Language Detector are stable since Chrome 138, and the Prompt API went stable in Chrome 148 with multimodal input and JSON-schema-constrained output.
  • WebMCP (origin trial Chrome 149–156, targeting stable at 157) lets pages register agent-callable tools via an imperative JS API or declarative HTML form annotations, gated by origin isolation and a tools Permissions Policy.
  • MCP Apps links tools to sandboxed ui:// HTML resources via _meta.ui.resourceUri; A2UI v0.9.1 covers the complementary declarative-JSON case for untrusted or cross-platform agents.
  • Browser inference is fast enough for real UX on desktop: published WebLLM numbers show ~41–71 tok/s on an M3 Max (roughly 80% of native), ~62 tok/s on an RTX 3060, and ~15 tok/s on an Intel Iris Xe — versus ~4–9 tok/s on flagship phones. Variance across hardware is large, so measure on your users’ machines, not the blog author’s.
  • The practical pattern today: capability-detect built-in AI, register 3–5 high-value WebMCP tools, render tool results as structured cards, and keep a server-LLM fallback. Test it with a golden-task eval set, not vibes.
  • This article is the browser-native companion to From Chat to Agent UI and Agent-Ready Infrastructure: those covered the server side; this one covers what happens inside the browser tab.

What You Will Learn Here

  • What Chrome’s built-in AI APIs can and cannot do today, and what hardware they require
  • What WebMCP is, how the imperative and declarative APIs work, and how to register your first tool
  • How MCP Apps and A2UI split the “tool result UI” problem, and which to pick
  • How the three layers compose into a browser-native agent runtime
  • How to build an inline copilot: a runtime chat sidecar backed by the browser LLM and page tools
  • What the benchmarks say about in-browser inference on desktop and mobile, and how to evaluate models on your tasks
  • A decision guide for what to build now versus what to wait on

Why the Browser Is Becoming an Agent Runtime

Step back and notice the shape of what shipped:

LayerQuestion it answersStandard / APIStatus (Sept 2026)
Reasoning”Where does the model run?”Prompt API + task APIs (Summarizer, Translator, …)Stable (138/148)
Actuation”How does the agent touch my page?”WebMCPOrigin trial 149–156
Presentation”How do results render?”MCP Apps (SEP-1865), A2UIFinal / v0.9.1 production

Each layer is useful alone. Together they close a loop that previously required a server round-trip, an API key, and a bespoke frontend: the page provides context and tools, the browser provides the model, and the result renders as UI the user can act on.

WebMCP replaces actuation — the agent simulating mouse clicks and keystrokes like a human. Instead of reviewing each element to guess its purpose, the agent reads the purpose your site declares. Fewer steps, less interpretation, more reliability. And because tools execute visibly on the page, users keep trust and brands keep their design.

This is also a privacy and cost story. Summarization, classification, rewriting, and form-filling assistance can now run fully on-device: no tokens billed, no text leaving the machine, no network round-trip after warmup. The server LLM becomes the fallback for hard reasoning, not the default for everything.

Layer 1: Browser LLMs — The In-Tab Model

Chrome’s built-in AI: two API shapes

Chrome ships two flavors, and picking the right one matters:

Task APIs are narrow, opinionated, and stable. Summarizer, Translator, and Language Detector have been stable since Chrome 138 (announced at Google I/O 2025). Writer, Rewriter, and Proofreader remain in developer trial. Use these when your task matches exactly — they are tuned for the job and have the simplest integration:

// Summarizer API — stable since Chrome 138
// Requires HTTPS + transient user activation (a click) for create()
const availability = await Summarizer.availability();
if (availability === "available") {
  const summarizer = await Summarizer.create({
    type: "tldr",
    length: "short",
    format: "markdown",
    outputLanguage: "en-US",
  });
  const summary = await summarizer.summarize(longArticleText);
}

The Prompt API is the general-purpose escape hatch: direct access to the on-device Gemini Nano model with your own system prompt, multimodal input (text, image, audio), structured output via JSON schema or regex, streaming, and sampling controls (temperature/topK in extensions; a samplingMode enum in the web origin trial). It went stable for the web in Chrome 148 after an origin trial spanning Chrome 139–147.

// Prompt API — stable since Chrome 148
// The model downloads on first use; LanguageModel.availability() tells you the state
const availability = await LanguageModel.availability();
if (availability === "available") {
  const session = await LanguageModel.create({
    initialPrompts: [
      { role: "system", content: "You extract action items as JSON." },
    ],
  });
  const schema = {
    type: "object",
    properties: { items: { type: "array", items: { type: "string" } } },
    required: ["items"],
  };
  for await (const chunk of session.promptStreaming(notes, {
    responseConstraint: schema,
  })) {
    renderPartial(chunk); // stream into your UI
  }
}

A few hard requirements to design around (from the Summarizer docs, which apply to all foundation-model APIs):

  • OS: Windows 10/11, macOS 13+, Linux, or Chromebook Plus. No Android, no iOS, no non-Plus Chromebooks.
  • Disk: at least 22 GB free on the Chrome-profile volume.
  • Memory: strictly more than 4 GB VRAM on GPU, or 16 GB RAM + 4 CPU cores on CPU. (Audio input in the Prompt API requires a GPU.)
  • Context: secure context (HTTPS), top-level window or same-origin iframe unless delegated, and transient user activation for create().
  • The model downloads separately on first use — budget for a large one-time download and show progress.

Note the honesty in the Chrome Status entry: the on-device model “is never trained on, and does not have access to, any local user-specific data.” Your page context reaches it only through what you pass in the prompt. That is a feature for privacy reviews.

The portable alternative: WebLLM and Transformers.js

Chrome’s APIs are Chrome-only. If you need Firefox, Safari, or a model Chrome doesn’t ship, the open path is WebLLM (mlc-ai/web-llm): an open-source JavaScript engine that runs 4-bit quantized open models (Llama, Phi, Qwen, Mistral families) on WebGPU, with an OpenAI-compatible chat API. Its sibling Transformers.js (Hugging Face) covers the same ground with a wider model zoo and WebGPU/WebAssembly backends.

The tradeoff is clean:

  • Built-in AI: zero model hosting, browser-managed downloads and updates, native-optimized performance — but Chrome-only, one model family, and eligibility-gated hardware.
  • WebLLM / Transformers.js: any Chromium/soon-Firefox browser with WebGPU, any supported open model — but you ship megabytes-to-gigabytes of weights, manage caching yourself, and eat first-run shader-compilation cost.

WebGPU coverage as of September 2026: Chrome/Edge 113+ on desktop, Chrome for Android 121+ on Android 12+ (Qualcomm/ARM GPUs first, more vendors landing through 2026), Safari 26+ on macOS Tahoe and iOS 26 with WebGPU on by default, Firefox support landing but slower (implementation status). The gap is no longer support — it is capability: mobile GPUs run small models slowly and sometimes crash on large ones. Treat browser inference as universal-but-tiered: same code path, wildly different ceilings.

What the benchmarks actually say

These numbers come with a caveat: browser inference speed depends on GPU, driver, thermal state, and quantization. Treat them as order-of-magnitude guidance, then measure on your own hardware:

Model (4-bit)EngineHardwareDecode throughputSource
Llama-3.1-8BWebLLM 0.2.75M3 Max, Chrome Canary41.1 tok/s (71% of native MLC-LLM)WebLLM paper
Phi-3.5-mini 3.8BWebLLM 0.2.75M3 Max, Chrome Canary71.1 tok/s (80% of native)WebLLM paper
Qwen2.5-1.5BWebLLMM3 / RTX 3060 / Iris Xe~45 / ~62 / ~15 tok/sindependent test, early 2026
Llama-3.2-3BWebLLM 0.2.80RTX 4050 / RTX 4090~46 / ~38 tok/s (yes, inverted)web-llm issue #773
RedPajama 3B (Q4)WebLLMPixel 7, Chrome Android~4.4 tok/s decode (~1.2 prefill)web-llm #209
Llama-3.2-3B (Q4)WebLLMPixel 8 Pro, Chrome Android~5.1 tok/s decode (~5.4 prefill)web-llm #759
Llama-3.2-1B (4-bit)WebLLM 0.2.84Android phone, Chrome~9 tok/s decode; 76s TTFT on a 1200-token promptLudion, 2026

Three lessons hide in that table:

  1. The browser keeps ~70–80% of native throughput. The WebGPU tax is real but modest — browser deployment is genuinely viable, not a party trick.

  2. The floor is what matters. 15 tok/s on integrated Intel graphics is fine for background summarization and unusable for a real-time typing companion. Design streaming UX that degrades gracefully.

  3. Scaling is non-linear and sometimes inverted. A 4090 scoring below a 4050 on the same model and browser version is the whole argument for benchmarking on representative hardware instead of trusting spec sheets. Suspected culprits: shader-cache state, power policy, and WebGPU backend maturity. Read the issue thread before you build a benchmark page.

  4. Mobile is a different computer, not a slower desktop. Flagship phones decode at ~4–9 tok/s — background summarization, not real-time companions. The failure modes are mobile-specific: WebLLM hard-gates large models on Android behind a maxStorageBufferBindingSize heuristic because tabs crash past it; an iPhone 11 Pro Max can report webgpu: true with f16 support yet complete zero inference runs; and a 1200-token prompt can take 76 seconds to first token while a 52-token prompt takes under 4. On mobile, benchmark — never just feature-detect.

Also budget for cold start: model load from cache runs 2–6 seconds depending on hardware, and the very first inference after load takes 2–3× longer while WebGPU compiles shaders. Load in a Web Worker, trigger proactively, and run a throwaway warmup inference behind your loading screen.

Layer 2: WebMCP — Pages That Expose Tools

What it is and where it stands

WebMCP is a proposed web standard (incubated in the W3C Web Machine Learning Community Group — not on the Standards Track yet) that lets a page register tools callable by browser-based AI agents. Chrome’s timeline: dev trial at 146, origin trial 149–156 (desktop, Android, and WebView), targeting stable at 157. For local development, flip chrome://flags/#enable-webmcp-testing.

One subtle point the explainer stresses: despite the name, WebMCP does not prescribe MCP wire format. The browser can surface page tools to its agent via MCP, proprietary function-calling, or anything else. WebMCP is the page-to-browser contract; what happens behind it is the browser’s business.

Two APIs: imperative and declarative

The imperative API is plain JavaScript on navigator.modelContext. You register tools with a name, a human/agent-readable description, a JSON-schema input, and an execute handler that reuses your existing frontend logic. One caution: the API shape was still settling in early 2026. Batch provideContext/clearContext were removed around March 2026 in favor of individual registration, so verify against the explainer before you ship.

// Illustrative — verify the exact shape against the current explainer
if ("modelContext" in navigator) {
  navigator.modelContext.registerTool({
    name: "searchProducts",
    description:
      "Search the product catalog by keyword. Returns matching products with name, price, and availability.",
    inputSchema: {
      type: "object",
      properties: {
        query: { type: "string", description: "Search keywords" },
        category: {
          type: "string",
          enum: ["all", "clothing", "electronics", "books"],
        },
      },
      required: ["query"],
    },
    execute: async (input) => {
      // Reuse the same store your UI already uses
      const results = await productStore.search(input.query, input.category);
      return { products: results, total: results.length };
    },
    annotations: { readOnlyHint: true },
  });
} else {
  console.debug("[webmcp] not supported — page still works for humans");
}

The declarative API annotates ordinary HTML forms so agents know how to fill and submit them — no JavaScript required. Try the Le Petit Bistro demo for this path, or the imperative demos (zaMaker, a React travel-booking sample) with source linked from the docs. Angular has experimental support too.

The security model, in one paragraph

WebMCP is gated twice: origin isolation (unavailable in documents with document.domain relaxation / Origin-Agent-Cluster: ?0) and the tools Permissions Policy (defaults to self; cross-origin iframes need allow="tools"). Sensitive actions should request user confirmation, and the whole API is designed for human-in-the-loop local workflows, not headless scraping — headless may work, but it is not the design center. Read the tool security guidance linked from the docs before exposing anything that spends money or mutates state.

The honest limitations

The Chrome docs name three, and all three shape your architecture:

  1. Discovery requires a visit. Browsers must navigate to your page and run JS to learn your tools. Manifest-based discovery (declare tools in the web app manifest so agents can reason about relevance with one HTTP GET) is a discussed future, not a present feature. For the server-side half of discoverability — llms.txt, MCP Server Cards — see Agent-Ready Infrastructure.
  2. Complex pages need refactoring. If your app state lives in five uncoordinated stores, your execute handlers will be glue over chaos. Tools force you to find — or build — a coherent action layer.
  3. Humans stay in the loop. Confirmation dialogs for sensitive tools are a feature, not friction to route around.

Layer 3: MCP UI — Tool Results That Render

A browser LLM that calls your WebMCP tool gets JSON back. JSON is for models; humans want to sort, filter, preview, and approve. That last mile is the MCP UI layer, and it has now split into two standardized answers:

MCP Apps (SEP-1865, status Final) is the “expressive” answer. A tool links to a predeclared HTML resource via _meta.ui.resourceUri (the ui:// scheme). The host fetches it with resources/read, renders it in a sandboxed iframe with deny-by-default CSP, and talks to it over JSON-RPC 2.0 via postMessage. The ext-apps package provides both sides: registerAppTool / registerAppResource on the server, the App class in the view. (The full host architecture is covered in From Chat to Agent UI — here you only need the pairing rule below.)

A2UI (v0.9.1 production, v1.0 release candidate) is the “safe and portable” answer: the agent sends declarative JSON (surfaces, components, data bindings) and the client renders with its own trusted native components — no iframes, no executable code from the agent. I covered it in depth in How to Build Agent-Driven Interfaces with A2UI; since that article, v0.9.x hardened transports (including A2UI-over-MCP) and v1.0 (spec updated June 8, 2026) adds client-to-server RPC.

Which one should you pick?

MCP AppsA2UI
PayloadHTML+JS in sandboxed iframeJSON component descriptions
Trust modelSandbox + CSP + auditable bridgeNo agent code executes at all
StylingFull control (it is your HTML)Client catalog owns styling
PortabilityHosts that implement MCP AppsWeb, Flutter, Angular, native
Best fitRich interactive widgets from trusted/first-party toolsUntrusted, remote, or multi-platform agents

For this stack, the natural pairing is: WebMCP tools whose results render as MCP Apps views when the tool ships its own UI, or as your page’s own components when the page itself is the renderer (more on that in the copilot pattern below). A2UI enters when the agent is remote or untrusted — the same trust-boundary rule from the earlier article still holds.

How the Three Layers Fit Together

Here is the full loop, from user intent to confirmed result, running (mostly) inside one browser tab:

flowchart TD
    subgraph Page["Your web page"]
        Tools["WebMCP tools\n(search, checkout, diagnostics)"]
        Surfaces["Structured surfaces\n(cards, tables, approval dialogs)"]
        State["App state + stores"]
    end
    subgraph Browser["Browser runtime"]
        Model["On-device LLM\n(Prompt API / Gemini Nano)"]
        Agent["Browser agent\n(tool routing + policy)"]
    end
    subgraph Host["UI host (tab or agent client)"]
        View["MCP Apps view / native components\n(sandboxed iframe or A2UI render)"]
    end

    User["User intent\n(chat, click, or inline selection)"] --> Model
    Model --> Agent
    Agent -->|"discover + call"| Tools
    Tools <--> State
    Tools -->|"JSON result"| Agent
    Agent -->|"render UI resource"| View
    View -->|"user acts (sort, approve, edit)"| Surfaces
    Surfaces --> State
    Surfaces -->|"updated context"| Model

Compare that to the pre-2026 version of the same flow. The prompt went to your server, the server called a frontier model, the model called tools on your MCP server, JSON came back, and your frontend hand-rendered it. Four network hops and an API bill — for what is now substantially a local operation.

Each layer degrades independently. No on-device model? Fall back to a server LLM that calls the same WebMCP tools through the browser agent. No WebMCP? The page still works for humans (progressive enhancement, not a dependency). No MCP Apps host? Render the JSON with your own components. Design the seams, not just the happy path.

Desktop vs Mobile: Where Each Layer Stands

The article so far reads desktop-first. Here is the honest split as of September 2026 — because the three layers are not equally mobile-ready, and the asymmetry shapes your architecture:

LayerDesktopMobileVerdict
Built-in AI (Prompt API, Summarizer, …)Stable (138/148)Not supported on Android or iOS; Android implementation prototyped, no ship date (intent to ship)Desktop-only for now
WebLLM / Transformers.js15–200 tok/s depending on GPU and engine~4–9 tok/s on flagships; 1–3B models; crash risk past memory limitsWorks, within narrow bounds
WebGPU itselfMature in ChromiumDefault-on in Safari 26 / iOS 26 and Chrome Android 121+ (Android 12+, mostly Qualcomm/ARM GPUs); older devices get nothing (status)Check navigator.gpu, then benchmark anyway
WebMCPOrigin trialOrigin trial includes Android + WebViewThe actuation layer is already mobile-ready
MCP AppsSpec FinalHost-dependent — verify per host and WebViewExpect variance (editorial inference)
A2UIProduction (v0.9.1)Designed for it (Flutter, native, cross-platform)The safe mobile UI bet

Three consequences fall out of that table:

  1. On mobile, the stack inverts: tools first, model second. WebMCP works on Android while built-in AI doesn’t, so the mobile pattern is a server LLM calling page tools through the browser agent — the same WebMCP tools you registered for desktop, a different brain behind them. Register tools once; swap the reasoner per platform.
  2. Feature detection lies on phones — benchmark instead. A 2026 cross-device test found an iPhone 11 Pro Max reporting webgpu: true with f16 support yet completing zero inference runs, and a Pixel 8a in an in-app browser reporting a full 4 GB ceiling while failing runs that succeed in Chrome (Ludion). WebLLM itself gave up on detection for large models on Android and hard-gates them behind a buffer-size heuristic (#209). Your availability check must be run a tiny warmup inference and time it, not read an adapter limit.
  3. Long context is the mobile killer, not decode speed. That same test measured ~9 tok/s decode holding steady on a phone — usable — but TTFT of 76–77 seconds on a 1200-token prompt versus ~3.8s on a 52-token prompt. Compact state snapshots (the copilot pattern’s rule) stop being good advice on mobile and become the difference between working and timing out.

Pros and cons, distilled:

  • Desktop pros: stable built-in AI, big memory/disk budgets, unmetered model downloads, mature tooling (inspector, chrome://on-device-internals). Cons: the floor (Iris Xe class) is still slow; the 22 GB free-disk and 16 GB RAM gates exclude thin clients.
  • Mobile pros: WebMCP + WebGPU already there on modern devices; the privacy story lands hardest on personal phones; sheer reach. Cons: no built-in AI; single-digit tok/s; 1–3B models with 1k-ish context; tab crashes past memory limits; thermal throttling on sustained inference; metered connections make multi-GB weight downloads hostile — cache aggressively and warn before downloading.

The practical takeaway: build one tool layer, two reasoner paths, and a benchmark-gated UI. Capability-detect built-in AI on desktop, WebGPU-warmup-gate on mobile, server fallback for everything else — and let measured TTFT, not the user agent, decide which features light up.

The Inline Copilot Pattern: Runtime Chat in Your App

The most concrete thing you can build with this stack today is a runtime chat sidecar: a copilot panel embedded in your app, backed by the browser LLM, grounded in page state, and able to act through WebMCP tools. Think “Cmd+K that can see the page and do things,” not “support chatbot in the corner.”

The shape

  1. A chat surface docked in your app (sidecar panel, command palette, or inline selection toolbar). It streams tokens from the Prompt API session.
  2. A system prompt with page context (a system entry in initialPrompts) — current route, selected item, visible filters, user role — refreshed as the page changes. Keep it small: on-device context is precious, so pass a compact state snapshot, not the DOM.
  3. Tool access via WebMCP. Register the 3–5 actions users actually ask for (“refilter this list,” “draft the reply,” “run diagnostics,” “fill this form from the ticket”). The browser agent routes model tool-calls to your execute handlers.
  4. Structured result rendering. When a tool returns data, render a card/table/approval inline in the chat and reflect the change in the page. The chat narrates; the surface does the work.
  5. Human confirmation for mutations. Read-only tools run freely; anything that writes, sends, or spends gets an inline approve/reject. This mirrors WebMCP’s human-in-the-loop design and keeps trust.

Minimal wiring sketch

// 1. Register page tools (WebMCP) — the copilot's hands
// 2. Open a grounded Prompt API session — the copilot's brain
async function createCopilot(pageSnapshot) {
  if ((await LanguageModel.availability()) !== "available") {
    return createServerCopilot(pageSnapshot); // fallback path
  }
  return LanguageModel.create({
    initialPrompts: [
      {
        role: "system",
        content: [
          "You are the inline assistant for this page.",
          "Answer from the page snapshot. Prefer calling a page tool over guessing.",
          "Never invent IDs, prices, or statuses — read them from tool results.",
          `Page snapshot: ${JSON.stringify(pageSnapshot)}`,
        ].join("\n"),
      },
    ],
  });
}

// 3. Streaming only (deliberately simplified: the browser agent mediates real tool calls, not this loop)
async function askCopilot(session, userText, onToken) {
  // In the full WebMCP flow, the browser agent discovers registered tools
  // and invokes them; here we show the page-side contract:
  // prompt -> (tool calls handled by agent) -> streamed answer + structured results
  const stream = session.promptStreaming(userText);
  for await (const chunk of stream) onToken(chunk);
}

Two honest notes about that sketch. First, the origin trial is still working out the exact page-side orchestration between a Prompt API session and WebMCP tool invocation. Today the browser agent (or an inspector-style harness) mediates discovery and calls — not your JS directly. Second, that gap is exactly why the Model Context Tool Inspector extension exists. Install it, talk to your registered tools in natural language (defaults to gemini-3-flash-preview), verify schemas, and watch structured results before you wire the real copilot UI.

What to register first

Start from support tickets and session replays, not imagination. The Chrome docs’ own examples generalize well:

  • Navigation rescue: find_form, go_to_section — agents (and users) get lost in complex IA.
  • Form bridging: submit_application, date_pick — map conversational data onto fussy human-first widgets.
  • Domain actions: filter_results, checkout, run_diagnostics — the 3–5 verbs that define your app.

One tool done well beats ten registered hopefully. Each tool needs a crisp description (the model reads it), a tight schema (loose schemas breed hallucinations), and error strings written for model comprehension (“No trip found for ID T-1042” beats “Error 400”).

Benchmarks and Personal Evals: Trust, but Measure

Published benchmarks tell you the ceiling. Only your own evals tell you whether your tasks clear the bar on your users’ hardware. Here is a harness that fits in an afternoon.

Step 1: Build a golden-task set (15–30 cases)

Cover the actual copilot workload, not generic chat:

  • Summarize: 5 real documents from your domain (ticket threads, reviews, meeting notes) with human-written reference key-points.
  • Extract-to-schema: 5 inputs with expected JSON (entities, dates, action items). Score with exact-match on fields, not vibes.
  • Rewrite/classify: 5–10 cases with labels (tone rewrite, priority classification, language detection).
  • Tool-routing: 5 utterances mapped to the correct WebMCP tool + arguments. This is the highest-value eval. A wrong tool call is worse than a hedged answer.

Step 2: Score with rubrics, plus a judge for prose

  • Deterministic checks for schema tasks: JSON parses, required fields present, values match expected.
  • Rubric checks for prose: 3–5 binary criteria per task (“mentions the refund amount,” “no invented dates,” “under 80 words”). Binary beats 1-5 scales for consistency.
  • LLM-as-judge only where rubrics can’t reach (fluency, tone). Calibrate the judge against your own ratings on 10 samples first. The full methodology in this devlog’s eval series (From Vibes to Scores, LLM-as-Judge biases and fixes) applies unchanged. The only difference: the system under test runs in a tab.

Step 3: Measure what users feel

Per task, record time-to-first-token, total latency, and output tokens. For WebLLM, read the usage object on the final chunk. That gives prefill and decode throughput for that machine. Run the matrix that matters:

  • Models: built-in (Prompt API) vs WebLLM candidates (e.g. Qwen2.5-1.5B vs Llama-3.2-3B vs Phi-3.5-mini).
  • Hardware tiers: discrete GPU, Apple Silicon, low-end integrated (the Iris Xe class).
  • Cache states: cold (first visit) vs warm (cached weights, compiled shaders).
  • Mobile tier: at least one flagship Android (Pixel/Samsung, Chrome) and, if your audience is iOS-heavy, one iPhone on iOS 26+. Expect single-digit tok/s and long-context TTFT in the tens of seconds — record TTFT at 50 and 1200 input tokens separately.

This snippet runs Steps 1-3 in DevTools. Paste it on an HTTPS page after a click:

// Requires: await LanguageModel.availability() === "available".
const goldenTasks = [
  { name: "extract-date", prompt: "Meeting moved to 2026-09-20, confirm the date.", check: (o) => o.date === "2026-09-20" },
  // ... add your 15-30 cases from Step 1, each with a check() from Step 2
];
const schema = { type: "object", properties: { date: { type: "string" } } };
const base = await LanguageModel.create({
  initialPrompts: [
    { role: "system", content: "Answer with JSON only: { date: string }." },
  ],
});
let passed = 0;
for (const task of goldenTasks) {
  const s = await base.clone(); // fresh context per task — sessions accumulate history
  const t0 = performance.now();
  let ttft = 0, text = "";
  for await (const chunk of s.promptStreaming(task.prompt, { responseConstraint: schema })) {
    ttft ||= performance.now() - t0;
    text += chunk;
  }
  const total = performance.now() - t0;
  const ok = task.check(JSON.parse(text));
  passed += ok ? 1 : 0;
  console.log(`${ok ? "PASS" : "FAIL"} ${task.name} | TTFT ${Math.round(ttft)}ms | total ${Math.round(total)}ms | ~${Math.round(text.length / (total / 1000))} chars/s`);
  s.destroy();
}
console.log(`Pass rate: ${passed}/${goldenTasks.length}`);
base.destroy();

Each task runs on a cloned session (base.clone()) so task N never sees tasks 1..N-1 — sessions keep full conversation history. The Prompt API reports no token counts, so chars/sec is the honest proxy. For real tok/s, run the same tasks through WebLLM and read the usage object. Swap the check() per task type: exact-match for schema tasks, rubric predicates for prose.

Step 4: Decide with a tradeoff table, not a single number

WorkloadOn-device verdictWhy
Summarize / extract / classify / rewriteUsually yesTask APIs are tuned for this; small models are strong here
Grounded Q&A over page snapshotYes, with evalsQuality hinges on snapshot design + “don’t invent” prompting
Multi-step tool orchestrationMaybe — test routing evalsSmall-model function-calling is the weakest link; keep tool sets small
Open-ended reasoning, long contextServer fallbackContext limits + quality gap vs frontier models

Re-run the golden set whenever Chrome updates the built-in model (check chrome://on-device-internals for the current version) or you swap WebLLM weights. On-device models move under your feet. Evals are the only way to notice.

Build vs Adopt: A Practical Decision Guide

Build now:

  • Task-API features (summarize, translate, detect) behind capability detection with server fallback. Stable since 138, well-documented, immediate user value.
  • Prompt API copilots for grounded, narrow tasks with JSON-constrained output. Stable since 148; keep prompts small and evals tight.
  • 3–5 WebMCP tools on your highest-friction flows, tested with the inspector extension (join the origin trial to ship to real users). Origin-trial-gated, but the shape (name + description + schema + handler) is unlikely to surprise you.
  • MCP Apps views for first-party tools where you control both ends. The spec is Final and the ext-apps SDK exists.

Prototype, don’t promise:

  • Full WebMCP-driven agent flows in production. Origin trial through Chrome 156 means API churn and limited reach. Remember: discovery requires a visit.
  • A2UI v1.0 features (client-to-server RPC, new catalog rules). Build on v0.9.1 shapes and isolate version handling in an adapter.
  • Cross-browser on-device inference. WebGPU is coming to Firefox and Safari, but today it is a Chromium story with graceful degradation elsewhere — track Baseline status before promising more.

Wait (and watch Chrome Status):

  • WebMCP stable (target 157) and any manifest-based discovery follow-up.
  • Prompt API sampling parameters on the web (origin-trial samplingMode enum; raw temperature/topK only in extensions).
  • Built-in AI on mobile. Chrome’s foundation-model APIs remain desktop-only (Android implementation prototyped, no ship date). But WebGPU inference on mobile works today within narrow bounds — see the desktop-vs-mobile table — so “mobile” is a tier to measure, not a date to wait for.

Production Caveats Worth Their Own Checklist

  • Hardware variance is the dominant risk. Your M3 Max numbers mean nothing to an Iris Xe user. Gate heavy features on measured throughput, not user-agent sniffing.
  • Model updates are silent pushes. Chrome refreshes Gemini Nano out-of-band. Your golden evals are the regression suite. Schedule them.
  • Permissions fatigue is real. Every sensitive tool confirmation is a chance to annoy. Batch approvals, explain consequences inline, and keep read-only tools frictionless.
  • Tool descriptions are attack surface and UX copy at once. They steer the model. Write them carefully, review them like prompts, and never trust tool inputs without validation in execute.
  • Accessibility and no-AI paths are non-negotiable. Every copilot action needs a clickable equivalent. Every AI-generated summary needs a “show source” affordance. Progressive enhancement is the whole pitch of this stack. Honor it.
  • Measure cost honestly. On-device isn’t free: gigabytes of downloads, battery drain, and your support burden when a user’s device can’t run the model. Instrument availability outcomes (available / downloadable / unavailable) in analytics.

The Real Shift

The last three years taught frontend developers to call models over HTTP and render the text. The next three teach a different reflex: reach for the tab first. The model is becoming a browser capability like geolocation or WebGL — detected, permissioned, and progressively enhanced. Pages stop being documents that agents squint at. They become tool providers with declared, typed, testable interfaces. The chat log stops being the product surface. It becomes the narration track over structured work.

That inverts a surprising amount of frontend architecture. State snapshots become prompt context. Action layers become tool manifests. Eval harnesses join the component test suite. The teams that start registering tools and running golden-task evals now — while WebMCP is still in trial and MCP Apps hosts are still multiplying — will have the action layers, the measurements, and the instincts ready when the stable flags flip.

Start small: one summarizer, three tools, fifteen eval cases. The future frontend stack fits in an afternoon’s prototype. And this time, it runs entirely in the tab.

Sources