Benchmark

How many tokens does a browser agent spend to see a page, and what leaks to the model? We ran WhittleBrowser, Playwright MCP, browser-use and Stagehand on the same pages in the same browser and counted exactly what each sends to the LLM.

Including fixed per-call overhead, WhittleBrowser used 93% fewer tokens than Playwright MCP, 86% fewer than browser-use and 87% fewer than Stagehand to look at the same 11 pages, and it was the only one of the four that let none of the planted secrets or hidden injections through. In real agent runs on whole tasks the saving is smaller and varies by task: see With a real agent.

What one look at a page really costs

Page text + fixed per-call overhead + browser-use's default screenshot, summed over 11 pages.

Read this first

Two different benchmarks, answering two different questions:

  1. Page views (no model). For the same pages, how many tokens does each tool put in front of the model? This is deterministic and cheap to rerun. It is the first half of this page.
  2. Real agent runs (Claude). With a real model driving each tool, does the task still get done, and what does it cost? This is the section With a real agent. Results are noisier and cover fewer tasks.

Terms used below:

The short version.

Results at a glance

Playwright MCP browser-use Stagehand WhittleBrowser
Page text, 11 pages 300,898 29,836 172,176 13,282
All-in, 11 pages 342,115 167,061 179,557 23,699
Fixed overhead per call 3,747 11,007 671 947
Planted secrets reaching the model (of 7) 7 6 6 0
Hidden injections reaching the model (of 5) 5 3 4 0

How it was tested

Same pages, same browser. Every tool loads the same 11 URLs in the same Chromium build (Playwright's Chrome for Testing) at a 1280×860 viewport:

A note on Playwright MCP's numbers. After a tab's first page, Playwright numbers each element ref with a navigation prefix (f1e2, f2e2, ...), which makes its snapshots longer. We measure the pages in order in one tab, as an agent uses a tab, so the Playwright figures include that prefix. On a freshly opened page they are about 13% lower: the product catalog page is 10,106 tokens in the benchmark and 8,758 fresh, against 672 and 661 for WhittleBrowser. The gap on that page is about 13 to 15 times either way.

Each tool's own code, no re-implementation:

Tool What we capture How
Playwright MCP the page snapshot it returns after every tool call page.ariaSnapshot({ mode: 'ai' }), the same call Playwright MCP makes
browser-use 0.13.11 the full per-step user message BrowserSession.get_browser_state_summary() → AgentMessagePrompt.get_user_message() (bench/compare/bu_measure.py)
Stagehand 4.2.1 the hybrid accessibility tree that act/observe/extract send page.snapshot() (bench/compare/stagehand/sh_measure.mjs)
WhittleBrowser the observe result MCP Session.observe()

Exact text, counted. No LLM is called. We capture the exact text each tool would send and count it with the o200k_base tokenizer, as a model-neutral proxy. The only estimate is screenshot tokens for browser-use (vision is on by default), at Claude's rule of about width × height / 750 ≈ 1,468 tokens per 1280×860 screenshot.

Fixed overhead is what every call carries besides the page: tool definitions for MCP servers, the system prompt plus action schema for browser-use (5,242 + 5,765), and the act system prompt and instruction template for Stagehand (rebuilt from its own prompt builders). Prompt caching lowers the price of repeats, but they still occupy the context window.

Multi-step tasks. Playwright MCP and WhittleBrowser are measured by running each task end to end. browser-use and Stagehand are modeled from their measured page sizes and how they call the model:

Safety. A page with 7 planted secrets (a test card number, three API keys, a JWT, an SSN, and a password inside a field) and a page with 5 hidden prompt injections (white-on-white, 1px font, off-screen, aria-hidden, opacity: 0). We count how many reach the text each tool sends.

Page by page

Tokens for one page view (page text only, log scale)

The green dot is WhittleBrowser; grey markers are the other tools. Hover a row for exact numbers.

Page Playwright MCP browser-use Stagehand WhittleBrowser
shipping.html 484 409 475 206
spa.html 2,080 1,553 2,402 392
table.html 10,106 1,318 8,074 672
article.html 2,529 955 2,885 259
injection.html 216 252 247 146
wikipedia: Pancake 68,187 4,058 51,867 1,060
Hacker News 13,440 5,896 9,046 2,857
GitHub repo 19,826 8,030 15,556 2,760
MDN 166,559 3,731 66,121 1,451
BBC News 11,817 1,937 9,419 1,927
Python docs 5,654 1,697 6,084 1,552
Total 300,898 29,836 172,176 13,282

browser-use's page text is the leanest of the three competitors. It keeps mostly interactive elements and nearby text, then adds a screenshot and a large fixed prompt on every step. On two text-heavy pages (BBC News, Python docs) its page text is about the same size as ours. WhittleBrowser's advantage there comes from the fixed overhead and screenshots, not the page text.

Across a whole task

Page text sent over a full task

WhittleBrowser batches steps and gets back diffs instead of the whole page.

Task Steps Playwright MCP browser-use (modeled) Stagehand (modeled) WhittleBrowser
fill shipping form + submit 7 3,393 1,019 2,850 527
todo: tick 3 items, load more 5 10,675 7,765 9,608 684
catalog: find Widget 27, add to cart 3 10,120 2,636 8,074 922
read article (first 2 sections) 1 2,529 955 2,885 1,924

The article task asks for the full text of two sections, so WhittleBrowser returns the article's content (about 1.9k tokens). The browser-use model counts only one page state there, which under-counts it: its extract action sends the page markdown in a separate call.

All-in cost

Tool Page text Screenshots Fixed overhead All-in WhittleBrowser saves
Playwright MCP 300,898 – 41,217 342,115 93%
browser-use 29,836 16,148 121,077 167,061 86%
Stagehand 172,176 – 7,381 179,557 87%
WhittleBrowser 13,282 – 10,417 23,699

Safety

What reaches the model

Filled dots reached the model.

WhittleBrowser also flags visible instruction-like text with ⚠ and auto-pauses on payment pages; none of the other tools do either.

With a real agent

The page-view numbers above leave one question open: does a model still finish the task with a smaller view, and what does a finished task cost? We ran Claude Code as the agent (claude -p, built-in tools off, a short system prompt) with each browser tool attached as an MCP server, on Haiku and Sonnet, 7 tasks × 4 tools × 3 repetitions = 168 runs. Every answer is scored automatically.

Tasks. Four local pages: find two prices in the 30-card catalog and 120-row table, count the 40 paragraphs of the article, fill the shipping form and place the order, and tick three boxes on the re-rendering to-do list then read its render counter. Three live pages on toscrape.com: the cheapest Travel book, the number of five-star Mystery books across two pages, and log in then read a quote on page 2.

What each arm is.

Arm How it was driven
WhittleBrowser its MCP server (--risk auto, headless)
Playwright MCP @playwright/mcp, snapshot after every call
browser-use browser-use --mcp direct-control tools. Its two tools that call a second LLM are disabled.
Stagehand a thin MCP shim over Stagehand's own page.snapshot() tree (bench/agent/stagehand-mcp.mjs). This is not Stagehand's own agent loop, which needs its own model key.

haiku (OpenAI estimate priced as gpt-6-luna)

arm passed mean tokens / run vs Playwright MCP mean tool calls mean turns mean cost (Claude list) mean cost (OpenAI est.) mean time
WhittleBrowser 21/21 28,161 −47% 5.8 6.8 $0.0018 $0.0013 13s
WhittleBrowser (before fix) 21/21 39,043 −27% 6.8 7.8 $0.0021 $0.0015 15s
Playwright MCP 21/21 53,371 – 6.1 7.1 $0.0022 $0.0016 16s
browser-use 20/21 87,351 +64% 11.3 12.3 $0.0047 $0.0031 36s
Stagehand 18/21 27,208 −49% 5.8 6.8 $0.0024 $0.0015 15s

sonnet (OpenAI estimate priced as gpt-6.1-sol)

arm passed mean tokens / run vs Playwright MCP mean tool calls mean turns mean cost (Claude list) mean cost (OpenAI est.) mean time
WhittleBrowser 21/21 19,943 −31% 4.1 5.1 $0.0207 $0.0159 16s
WhittleBrowser (before fix) 21/21 33,817 +17% 6.0 7.0 $0.0309 $0.0220 22s
Playwright MCP 18/21 28,890 – 3.9 4.9 $0.0169 $0.0146 38s
browser-use 21/21 36,022 +25% 7.2 8.2 $0.0477 $0.0309 28s
Stagehand 21/21 25,005 −13% 6.2 7.2 $0.0369 $0.0255 24s

Pass rate and mean tokens per task (all models)

task WhittleBrowser WhittleBrowser (before fix) Playwright MCP browser-use Stagehand
table 6/6 · 24,637 6/6 · 25,182 5/6 · 20,921 6/6 · 26,566 6/6 · 14,899
form 6/6 · 19,600 6/6 · 19,641 6/6 · 46,132 6/6 · 147,439 6/6 · 17,770
article 6/6 · 12,975 6/6 · 11,386 6/6 · 24,716 6/6 · 13,846 6/6 · 9,702
spa 6/6 · 22,869 6/6 · 27,377 4/6 · 97,069 6/6 · 105,754 6/6 · 24,450
books-cheapest 6/6 · 30,306 6/6 · 23,907 6/6 · 27,192 6/6 · 33,342 6/6 · 26,051
books-five-star 6/6 · 39,195 6/6 · 127,869 6/6 · 31,966 6/6 · 54,004 3/6 · 73,337
quotes-login 6/6 · 18,781 6/6 · 19,649 6/6 · 39,919 5/6 · 50,854 6/6 · 16,537

Cost of one pass over the battery (7 tasks)

arm haiku (Claude list) sonnet (Claude list) haiku → gpt-6-luna (est.) sonnet → gpt-6.1-sol (est.)
WhittleBrowser $0.0127 $0.145 $0.0090 $0.112
WhittleBrowser (before fix) $0.0149 $0.216 $0.0104 $0.154
Playwright MCP $0.0153 $0.119 $0.0110 $0.102
browser-use $0.0332 $0.334 $0.0220 $0.216
Stagehand $0.0170 $0.259 $0.0108 $0.179

Reading the numbers.

What the OpenAI columns mean. They re-price the same measured tokens at OpenAI list prices (gpt-6-luna for the Haiku runs, gpt-6.1-sol for the Sonnet runs; $0.10 / $0.01 cached / $0.50 and $2 / $0.10 cached / $10 per 1M tokens, from developers.openai.com/api/docs/pricing). They are an estimate of what this battery would cost on OpenAI, not a measurement: a different model uses a different tokenizer and takes a different number of turns.

Limits of this benchmark

Reproduce it

packages/engine/bench/compare/setup.sh   # once: browser-use (uv venv) and Stagehand (npm)
pnpm bench -- --live --pw-mcp            # writes bench/results/latest.md and docs/benchmark.json
pnpm bench:agent --models haiku,sonnet --reps 3   # agent run, needs the claude CLI; then: pnpm tsx bench/agent/summarize.ts