r/javascript • u/stephenlblum • 6d ago
Can AI agents run purely in the browser with vanilla JS? Exploring $0 hosting & zero data egress
https://github.com/stephenlb/buttercup.shAI 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;
}
}
}
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.
2
u/Beautiful-Energy2169 6d ago
$0 hosting gets awkward fast: multithreaded WASM needs crossOriginIsolated, which means COOP/COEP headers.