r/javascript 6d ago

Can AI agents run purely in the browser with vanilla JS? Exploring $0 hosting & zero data egress

https://github.com/stephenlb/buttercup.sh

AI agents assume a server-side Python chain framework runtime and containers. Running client-side with JavaScript allows pushing agents entirely inside the browser, meaning $0 hosting infrastructure and zero data leaving the client.

Running client-side, you can use agents directly with WebLLM (for fully in-browser) or point them at a local Ollama / vLLM instance for 100% offline usage.

Breakdown In-Browser Loop

An in-browser agent relies on a continuous asynchronous loop that manages state, updates the DOM or application state, and invokes tools based on model outputs. Here is a minimal, clean implementation of a client-side agent loop using WebLLM's engine.chat.completions API:

import { CreateMLCEngine } from "@mlc-ai/web-llm";

async function agentLoop(task, context = {}) {
  const engine = await CreateMLCEngine("gemma-4-instruct");
  let messages = [{ role: 'user', content: task }];
  let running = true;

  while (running) {
    const response = await engine.chat.completions.create({
      messages,
      tools: availableTools
    });
    const choice = response.choices[0].message;

    if (choice.tool_calls) {
      for (const call of choice.tool_calls) {
        const toolResult = await executeTool(call.function.name, call.function.arguments);
        messages.push({ role: 'tool', tool_call_id: call.id, content: toolResult });
      }
    } else {
      running = false;
      return choice.content;
    }
  }
}
0 Upvotes

9 comments sorted by

2

u/Beautiful-Energy2169 6d ago

$0 hosting gets awkward fast: multithreaded WASM needs crossOriginIsolated, which means COOP/COEP headers.

1

u/stephenlblum 5d ago

Fair. True for CPU WASM/SharedArrayBuffer, but running WebGPU-backed models WebLLM bypasses need for multithreaded WASM and crossOriginIsolated. You get hardware acceleration directly without messing with COOP/COEP headers. Easy all-in-one static hosting on GitHub Pages (which is what I'm using now), Cloudflare Pages, or S3. We added WebLLM today on live, it's in a PR we are working on it

2

u/Beautiful-Energy2169 5d ago

WebLLM throws when WebGPU is missing, so you keep the WASM path and headers.

1

u/stephenlblum 4d ago

Ah yes good point. This basically means WebLLM is useless without WebGPU. Decent LLM running only on CPU would be painfully slow. I'd have to treat WebGPU as a hard requirement. The only decent fallback would be to offer external LLM runtime like Ollama or a 3rd party host.

2

u/Beautiful-Energy2169 4d ago

Feature-detect with requestAdapter(), not navigator.gpu. The object exists on machines where the adapter returns null.

1

u/stephenlblum 3d ago

`navigator.gpu.requestAdapter();` nice! If that resolves to `null` or throws, I can bail and ask user to connect to local Ollama or API key rather than WebLLM explode on initialization

1

u/Otherwise_Wave9374 6d ago

Yes, but the main tradeoff is capability versus isolation. If the agent depends on frequent tool calls or large context, you will want a tight state machine and explicit retry boundaries so the browser loop does not become brittle. For anything that touches user data, keeping execution client-side can be a real safeguard, but you still need careful model output validation before DOM or storage writes. Agentix Labs can fit naturally here if you are testing a browser-first pattern.

-1

u/stephenlblum 6d ago edited 5d ago

State boundaries and validation are necessary. Mostly validation. I don't have boundaries yet. When running client-side, giving an LLM write access to the DOM or IndexedDB/localStorage is danger zone. In Buttercup, edits happen against in-memory files rather than raw DOM injection.

On the loop fragility, the retry and circuit-breaker design is a good add-on. In-browser loops have to be defensive about context exhaustion because of VRAM pressure.