Skip to content
Blog

Wasm-Powered Agents: Running Secure Gemini 3 Nano Logic in the Browser

Ship an entire AI agent as a single HTML file. Learn the Wasm-powered agent pattern: Pyodide vs WebLLM, sandboxing agent-generated code in WASM kernels, and wiring MCP tools from a browser tab.

Published on August 2, 2026

AI Assistant

What if an AI agent could ship as a single HTML file — no server, no Docker, no install? That’s the Wasm-powered agent pattern: WebAssembly gives near-native speed and a sandbox “by construction,” so the entire agent loop runs inside a browser tab while the model runs against a local Nano SLM. Your data never leaves the device.

In this tutorial, you will learn the two ways to run the model (Pyodide + OpenAI-compatible API, or fully in-browser WebLLM), how the Wasm boundary doubles as the sandbox for agent-generated code, and how to wire MCP tools from inside the tab.

Why Wasm for Agents

  1. Sandboxed by construction. The browser isolates the agent — no VM, no container, no host-process access.
  2. Zero install. One HTML file; open it and run. No server configuration, no Docker containers, no complex deployments.
  3. Privacy. Inference against a local model means no data leaves the device.
  4. Portable. The same agent file runs on any OS with a modern browser.

A collection of simple, single-file agents that run in a browser tab, in an environment that is sandboxed by construction, and without the need to manually install extra libraries or development frameworks. — Mozilla AI Blog (https://blog.mozilla.ai/wasm-agents-ai-agents-running-in-your-browser/)

The Architecture

flowchart TD
    A["Single HTML file"] --> B["UI (page)"]
    A --> C["Pyodide (WASM)<br/>runs agent code"]
    A --> D["config.js<br/>model endpoint"]
    C --> E["OpenAI-compatible API"]
    E --> F["Local model<br/>(Ollama / LM Studio)"]
    E --> G["Cloud model"]
    C --> H["Tools (fetch, exec)"]

The agent code is a Python script executed through Pyodide, driven by a framework like the OpenAI Agents SDK. Model access is an OpenAI-compatible server — a local Nano model via Ollama, or a cloud key.

Two Ways to Run the Model

1. External inference over an OpenAI-compatible API (Pyodide route)

Point the agent at Ollama, LM Studio, vLLM, or HuggingFace TGI. For a local model, run Ollama with a large context and CORS enabled:

OLLAMA_CONTEXT_LENGTH=40000 OLLAMA_ORIGINS="*" ollama serve

Make sure you run with an appropriate context length and allowing CORS Origins. — mozilla-ai/wasm-agents-blueprint (https://github.com/mozilla-ai/wasm-agents-blueprint/)

2. Fully in-browser inference (WebLLM route)

WebLLM loads a quantized model directly in the tab, using Web Workers + Comlink for non-blocking processing. A common trick is matching the model size to the language runtime:

Agent runtimeModelRAM
Rust (wasm32)DeepSeek 8B f326GB+ VRAM
Go (wasm32)Qwen2 7B4–5GB VRAM
Python (Pyodide)Phi-22–4GB
JavaScriptTinyLlama1–2GB

WebLLM integration for client-side LLM inference, with Web Workers for non-blocking background processing and Comlink for seamless Web Worker communication. — wasm-browser-agents-blueprint (https://github.com/hwclass/wasm-browser-agents-blueprint)

The Wasm Sandbox as the Agent Boundary

Beyond running the loop, Wasm is becoming the sandbox for agent-generated code. Three isolation tiers are emerging:

  1. In-process VmKernel — cheap, same-process.
  2. WASM kernels — QuickJS / Pyodide / Wasmtime; code executes with no host-process access.
  3. Remote microVM — strongest isolation, cloud-backed.

A CapabilityManifest plus a runtime firewall sits across all tiers: vet tool calls before execution, enforce per-call policy, and track taint across results.

Run agent-generated code in an isolated WASM kernel — no host-process access. — WasmAgent (https://github.com/WasmAgent/wasmagent-js)

import { sandboxedJsTool } from "@wasmagent/ai";
import { JSKernel } from "@wasmagent/kernel-quickjs";

const codeTool = sandboxedJsTool({ kernel: new JSKernel() });
// Drop into any AI SDK / LangChain / OpenAI Agents setup.

This matters because the dangerous part of an agent isn’t the model — it’s the code the model writes and runs. Wasm makes that code harmless by default.

MCP from the Browser

A Wasm agent isn’t cut off from tools. It can connect to local or remote MCP servers — and even turn the browser tab into an MCP backend that external agents use as a secure sandbox:

claude --tools "" --mcp-config '{"mcpServers": {"browser-sandbox": {"type": "http", "url": "http://localhost:3050/mcp"}}}'

While the agent runs in the browser, it can connect to local or remote MCP servers. We also provide an MCP Bridge that lets external agents use the browser sandbox as their backend. — agent-in-a-browser (https://github.com/tjfontaine/agent-in-a-browser/)

The CORS Trap

The #1 failure: tools that fetch cross-origin data, or a local inference server, get blocked by the browser. You must enable CORS on the inference server (OLLAMA_ORIGINS="*") and allow cross-origin on tool sources while testing.

This is a real tension: the sandbox that protects you also blocks legitimate tool access. Resolve it with an explicit allowlist, not by disabling CORS globally in production.

Code mixing information retrieved from different URLs is insecure by default and typically blocked by modern browsers. — Mozilla AI Blog (https://blog.mozilla.ai/wasm-agents-ai-agents-running-in-your-browser/)

A Minimal Working Example

<script type="module">
  import { pyodide } from "https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.mjs";

  const py = await loadPyodide();
  await py.loadPackage(["openai"]);          // agent SDK via micropip

  // The whole agent runs here, sandboxed by the browser.
  const agentCode = `
import asyncio
from openai import AsyncOpenAI
from openai.agents import Agent, Runner

async def main():
    client = AsyncOpenAI(base_url="http://localhost:11434/v1")  # Ollama
    agent = Agent(name="nano-agent", model="qwen3:8b", client=client)
    result = await Runner.run(agent, "Summarize this page")
    print(result.final_output)

asyncio.run(main())
`;
  await py.runPythonAsync(agentCode);
</script>

Reality Checks

  • Models are heavy. Not every machine runs 8B. Use smaller models or shorter context on constrained hardware.
  • Tool-calling skill varies. Not all models are good at tool calls; qwen3’s thinking mode (/think, /no_think) changes behavior.
  • Pyodide loading needs network even for local models (package download on first run).
  • Async in WASM needs JSPI (Chrome/Edge) or Asyncify/worker bridges (Safari/Firefox).

I can barely run the 1.7b model with a much shorter context on a Raspberry Pi 5 with 8GB RAM, and it does not return a response in time unless I increase the agent’s timeout value. — Mozilla AI Blog (https://blog.mozilla.ai/wasm-agents-ai-agents-running-in-your-browser/)

Conclusion

Wasm-powered agents move the agent loop into the browser: sandboxed by construction, zero install, and private when paired with a local Nano model. Run inference via an OpenAI-compatible local server (Pyodide) or fully in-tab (WebLLM). The same Wasm boundary is the emerging sandbox for agent-generated code — capability manifests, runtime firewalls, and isolated kernels. Handle CORS deliberately and pin tool-calling-capable models.

Next steps: try the multi-language blueprint (Rust/Go/Python/JS agents), add an MCP bridge so external agents reuse your browser tab as a sandbox, and experiment with qwen3:8b thinking mode to see how it changes tool-calling quality. A browser tab is now a legitimate, auditable agent runtime — and it ships as one file.

References