Home / Articles

Why Coding Agents Keep Reinventing the Browser

Why long-running coding agents converge on browser-shaped tooling: helper scripts, cookies as memory, compact page views, HTTP fallbacks and training-data bias.

Publisher: REACTAPP.TOP12 min read2722 words

This article is published in English.

Sources: Why Do AI Agents Love Building Web Browsers?

The browser that nobody asked for

Coding agents show a repeatable habit. With free access to the web, the power to write and execute code, and a job that runs for long enough, they tend to end up constructing something that resembles a browser. Wanting a browser isn't the motive. What the agent is after is a cheap, reusable tool that can open a URL, click, fill in forms, wait for data and extract it, hold on to cookies and sessions, and snap a screenshot after a failure. Viewed like that, it's just everyday engineering of the sort a human developer would do.

Chromium as a product isn't the destination. The agents are heading toward the function a browser serves: converting web protocols and conventions into operations.

Product versus abstraction

A browser in the Chrome or Firefox sense is a big application: tabs, an address bar, a rendering engine, a process model. None of that is what an agent wants. It wants the underlying job of turning how the web works into operations that can be issued.

People use that interface without noticing. A login page obviously asks for credentials, a button obviously can be pressed, and you click it, wait for the next page and carry on. An agent can't lean on that unspoken understanding, so it has to convert the same work into explicit operations. Ask a coding agent to work across arbitrary websites and you've handed it a problem whose natural abstraction is already known, so it drifts toward that boundary.

The modern web also isn't built to be consumed as a tidy API. Usually there's an HTML document wrapped in JavaScript, plus login sessions, cookies, redirects, client-side rendering, infinite scroll, popups, file uploads and asynchronous requests. A plain HTTP GET is only the start. Real tasks in that setting eventually call for a set of operations like these.

open(url)
click(selector)
fill(selector, text)
wait_for(selector)
extract(selector)
go_back()
screenshot()

That isn't a graphical browser, but it's undeniably a general-purpose interface to the web.

Why long tasks produce helpers

Short jobs can be handled ad hoc. A one-off lookup is brute-forced without any infrastructure, since it won't repeat and finishes in seconds.

"Find the current exchange rate."
"Look up this company's homepage."
"Find the opening hours of this restaurant."

Long-running work is different. Imagine an agent monitoring hundreds of sites every few hours, running a complicated workflow repeatedly, scraping pages while staying logged in, gathering evidence over several days or polling a service until something changes. If the model handles each click and each inspection, the cost balloons. A naive run looks like this:

  1. Open a browser, which is the tool that acquires the page.
  2. Inspect the page, with the rendered DOM as the source of observations.
  3. Find the search box, type the query, click Search and wait.
  4. Extract data.
  5. Reason about it.
  6. Repeat.

A capable agent responds the way a human developer would, by writing a reusable helper. The next run then goes:

  1. Run the script.
  2. Check whether the output still looks right.
  3. Call the model again only when it doesn't, for instance when a selector comes back empty, the page structure changed or the extracted data no longer matches what the task expects.

That's a large architectural change. The model stops performing every operation. It becomes the part that works out which procedure should run, while ordinary software uses the chosen tool and collects the observations. Put differently, the agent compiles its own experience into a script.

Explore in a browser, replay in code

A browser is an excellent place to explore. An agent can poke around, navigate, watch results and work out which sequence of actions accomplishes a goal. After that, reasoning through every action again is wasteful.

Say the agent learns that finding a product on a shopping site always means going to the home page, searching for "USB-C cables," applying filters for four stars and up, fast shipping and a price cap, paging through results, and extracting titles, prices and product IDs. There's no reason for a language model to rediscover those five operations on every run. It can turn them into a procedure and reuse it for similar problems. That does need a live session with cookies, so the agent settles on Puppeteer, Playwright or something similar as its page-acquisition tool.

async function getNewProducts(query) {
  const browser = await playwright.chromium.connectOverCDP(
    `wss://${AUTH}@brd.superproxy.io:9222`,
  );
  const page = await browser.newPage();
  await page.goto("https://example.com/products");
  await page.fill("input[name=q]", query);
  await page.click("button[type=submit]");
  await page.waitForSelector(".results");
  return extractResults(page);
}

The snippet connects Playwright to a remotely hosted browser over the Chrome DevTools Protocol. A hosted browser makes sense when a long-running agent must keep its session alive every few hours without the model involved. It requires account credentials formatted into a connection string, which the ${AUTH} placeholder stands for.

A repeatable structured extraction on a site with no prebuilt scraper doesn't even need a local Playwright file. The same workflow can be compiled into a hosted scraping tool: describe the data wanted in plain language, receive a generated collector and run it with no model in the click loop. Its interaction and parser functions turn out to be the same open, click, fill, wait, extract and screenshot job, just hosted.

Such a script fails once CSS selectors change, forcing the agent to explore afresh and write another. If the procedure sits inside a hosted tool, AI may be able to repair it without anyone rewriting selectors.

Human developers behave the same way. The first visit to a website teaches how it works, and after that nobody thinks about every click. When the site changes, you learn it again, though you keep that knowledge implicitly. An LLM has a far more expensive memory. If it has to rebuild the procedure from context each time, it pays for the same discovery over and over. So it saves the learned workflow in a script and runs that, until the page stops matching.

Cookies as memory outside the model

No language model can keep a long web session in its context, at least today. Writing a .py file still calls the model, whereas storing cookies doesn't. As a session proceeds, a browser piles up memory by itself, so it's already outside storage for the agent. It has three relevant parts:

  • The cookie jar, including HttpOnly cookies and ones JavaScript sets following a login or bot check. Often the model can't read these at all, yet the browser keeps them and attaches them to the next request.
  • Origin storage, meaning localStorage, sessionStorage and IndexedDB, which are almost entirely values written by the site.
  • Session history, the back and forward stack that makes back() or tabs.goBack() work.

Some state lives outside the browser store, such as a server-side session or a URL that doesn't exist until JavaScript runs. The agent needn't read that itself. It only has to drive a browser layer that already holds the session.

Safety risks of handing over a browser

When an agent can read any page and any cookie and drive a live session, prompt injection stops being a matter of better prompts and turns into an architectural hazard. Any system operating a live browser faces it, but coding agents are worse off since they can usually write and run what they come across. Limit what the agent does without a person watching, and begin with a sandbox.

A good template is Anthropic's computer-use checklist: use a dedicated VM or container, skip logins unless the task needs one, keep domain allowlists, and require a human to approve anything involving money or consent.

Why raw HTML makes a poor input

Raw pages are terrible input for a language model because there's so much noise. What the agent needs may be buried in thousands of tokens of markup, styling and application machinery. A good browser tool therefore returns a smaller space of actions and observations in place of the full HTML.

The figure shows the Document Object Model of an HTML page, the tree that a compact view or an accessibility tree has to shrink. A useful tool can transform it into something much smaller. Rather than a whole document, it might give the model a short outline like this.

Title: Search results
Links:
  [1] Home
  [2] Next page
Forms:
  search: input[name=q]
Buttons:
  submit: button[type=submit]
Main content:
  ...

Or it can expose an accessibility tree.

- navigation
  - link "Home"
  - link "Search"
- main
  - heading "Results"
  - list
    - listitem "Item 1"
    - listitem "Item 2"

That form is far nearer to what a reasoning model can use. The browser-shaped layer therefore does more than allow interaction: it squeezes the environment into a compact set of actions and observations. The agent sees just the pieces of the web relevant to its task, not the machinery behind page rendering. Context is saved, inference gets cheaper and mistakes often, though not always, drop.

There's a caveat: a full accessibility snapshot with every reference can end up bigger than the visible text. Compression only counts when the wrapper really returns a short, manageable list of controls. How short depends on the model. A small model can't use a raw DOM, and a strong one can but still pays for the tokens. Neither wants the full HTML.

When plain HTTP is enough

After the agent has learned how a site works, the cheapest reliable implementation may not involve browser automation at all. Sometimes the browser turns out to be doing something quite simple underneath. A "Next page" button might ultimately issue just this.

GET /api/products?page=2

When that's the case, rendering the page is pointless, and the agent can call the endpoint directly. The result is a ladder of options.

human interface
      ↓
browser automation
      ↓
discovered workflow
      ↓
direct HTTP/API calls   ← only if the site still allows it

Its value was in showing how the application behaves. After that's understood, the agent can strip away layers and sometimes drop to plain HTTP. An agent that never touches a JavaScript-heavy page can skip browser automation altogether and stick with HTTP, ideally with an unblocking service on standby for refused requests. Whatever the path, autonomous agents need a fallback, because a Cloudflare wall or anti-bot check is almost certain on the open web. With no inexpensive retry, the model wanders, spends tokens and drives up the bill.

Two complications apply.

First, plenty of sites can't be cut down to HTTP. Most won't let themselves be reverse-engineered: they throttle non-browser traffic, change selectors, fingerprint clients and add anti-bot checks or CAPTCHAs. For those, keep the full browser approach, whether local or hosted.

Second, a fallback doesn't always pay off. A documented public JSON API is structured, bounded and simple to log. An internal endpoint with no documentation that the agent worked out on its own is another thing: cheaper to call, yes, but harder for a person to audit than a script clicking a visible button, with thinner logs. A 200 reply packed with garbage is easy to overlook, while a broken page stands out.

Ultimately the agent has no loyalty to browsers. What it wants is the least expensive interface to its surroundings that still works:

  • If that interface is a JSON endpoint and the site honors it, use JSON.
  • If it needs JavaScript execution and session state, use a real browser through Puppeteer or Playwright, or a hosted one.
  • If it only needs downloaded HTML, use HTTP, with a proxy or unblocker if the GET is blocked.
  • For repeatable structured extraction on a site that lacks a ready-made scraper, compile the job into a hosted scraping workflow, where a code worker covers the HTTP rung and a browser worker covers the live session.
  • When what matters is an accessibility tree, hand that over directly.

Long-lived web agents usually carry several of these methods at once. The skill that counts is shifting up and down the hierarchy whenever the situation calls for it.

Does training data explain it?

The answer is mixed. Training data can account for a model reaching for Playwright or Puppeteer, yet it can't account for building browser-like tooling to begin with.

A 2026 Oxford and Microsoft study named BiasBusters tested a close relative of that question, namely whether exposure in training sways the tool a model selects. The team continued training a small model on a corpus heavy with metadata for a single weather API, one of several offering the same service.

  • Beforehand the model almost never picked that API, roughly 0.6% of the time, about what chance alone would give.
  • Following training on the skewed corpus it was chosen around 12% of the time, twenty times as often.

In short, exposure during training alone, whatever the task, measurably tilts which tool or product a model prefers. That's about picking a tool, though. The abstraction (open, click, fill, wait, extract as a shape, whichever library supplies it) is dictated by the environment. A model that had never seen Playwright would still need something to open pages, wait for content and report results, since interactive pages demand it.

Two separate things are at work:

  • The abstraction of open, click, fill, wait and extract is imposed by the environment. Pages that are interactive come with navigation, timing, controls and rendered output regardless of whether Playwright appears in any model's weights.
  • The tool, Playwright versus a home-grown client, comes from what the model has seen, as BiasBusters showed for interchangeable APIs.

Training data covers the latter and misses the former. It can't say why the problem keeps steering toward browser-like abstractions. The more basic reason is that the web is already arranged along those lines.

Adapters with the best return

Agents like Claude Code aren't really building browsers. They're building adapters between a reasoning system and an environment that happens to be browser-shaped.

Evolutionary biology has a concept called carcinization: the odd tendency of unrelated crustaceans to converge on a crab-like body over time.

Those species didn't conclude that being a crab was a smart plan. Their surroundings simply kept rewarding about the same answer to about the same problems. Coding agents may be following a similar path. They notice that the current approach is slow and write a script, add retries when it fails, add session persistence once authentication appears, and add DOM or accessibility extraction when pages grow too big to reason over.

The task requires the web.
        ↓
The web exposes a browser-shaped interface.
        ↓
The agent needs a way to operate that interface.
        ↓
Repeated operations make ad-hoc reasoning expensive.
        ↓
The agent writes reusable tools.
        ↓
Those tools accumulate state, retries, and abstractions.
        ↓
The tools start looking like a browser.
        ↓
Known workflows get compiled into deterministic code.
        ↓
The page changes so the agent re-explores.
        ↓
If an API exists, use it to get the data.
If not, and the site allows it, fall back to regular HTTP.
If not, stay on the browser.

Given enough rounds, the outcome looks strikingly like a small browser. That's a better mental model for autonomous agents than imagining digital people with a strange interest in browsers. They behave as good engineers do, studying a messy environment, spotting the interface it truly offers, and building abstractions around whatever is repetitive, stateful and costly. The web already has an old and highly successful abstraction for the purpose, and it goes by the name browser.