Same pain. Opposite architectures. Different right answers.
Stagehand (Browserbase, 22K★, 745K weekly npm downloads) and Tap both promise: AI helps you build a browser automation that doesn’t break next week. The pitches even sound similar — Stagehand says “Write once, run forever” and Tap says “Compile once. Run forever. Diff the drift.”
Same target. Opposite engineering choices. The split happens at one design decision — where the trust boundary lives — and once that’s made, the rest of the stack falls into place automatically. This page lays out both choices honestly so you can pick the right one for your task.
| Your task | Pick |
|---|---|
| Building an AI agent that browses the web for end users (no logged-in state) | Stagehand — cloud-first scales out, no local browser needed |
| Need 10K parallel scraping jobs against public pages | Stagehand + Browserbase — built for this pattern |
| Quick prototype where the LLM should figure things out at runtime | Stagehand — act("click on the price") is genuinely faster than compiling a plan |
| Scraping your own logged-in account (LinkedIn, Twitter, internal SaaS) | Tap — your cookies stay on your machine; cloud-vendor TOS doesn’t apply |
| HIPAA / SOC2 / regulated cohort where data can’t leave the boundary | Tap — runs entirely local, no vendor-side log to subpoena |
| Need an audit trail of every action your scraper takes | Tap — .tap.json is data; every op is grep-able |
| Internal / intranet sites, VPN-restricted | Tap — your laptop is on the VPN; the cloud isn’t |
| Want to monitor drift on existing Playwright/Puppeteer scripts without rewriting | Tap — adapters convert your source 1:1 |
| Worried about vendor lock-in / want a portable plan format | Tap — .tap.json is open spec, MIT |
Both lists are real. Pick the architecture that matches what you’re actually trying to do.
Stagehand Tap
───────── ───
┌─ trust boundary ─┐ ┌─ no external boundary ─┐
│ │ │ │
You ──► Cloud ──► Site You ◄──── Your CLI ────► Site
(cookies) (browser) (cookies) (your browser)
crossed no boundary crossed
to vendor no vendor logs to subpoena
That single architectural axis — where do logged-in cookies live during automation? — explains every other difference between the two products. Cloud-first SDKs answer “the vendor’s servers.” Local-first answers “only your machine.” Once you make that call, the rest follows: how AI participates, where the plan lives, what the failure modes look like.
| Dimension | Stagehand | Tap |
|---|---|---|
| Where it runs | Browserbase cloud (or your machine if self-hosted) | Your local Chrome (extension) or your local CLI |
| Plan representation | TypeScript code: act(), extract(), observe() calls embedded in your .ts |
Bare v2 Plan — 11-op closed union, pure data, no JS in the runtime path |
| AI in runtime? | Yes — every act() calls an LLM to choose the next action (with caching) |
No — LLM only at compile/forge time. Runtime is pure code. |
| Cookie / session storage | Browserbase stores them (encrypted at rest, but they cross a boundary) | Local browser only — never serialized, never transmitted |
| What “self-repair” means | LLM re-decides on retry when an act() fails |
Per-tap CEL snapshot_equivalent catches drift; re-running capture against the same site/name re-compiles the broken op |
| Plan portability | Locked to Stagehand SDK + Browserbase runtime | .tap.json runs on any Tap-compatible runtime (CLI, Chrome ext, future Playwright runtime) |
| Audit trail | Read TypeScript source + LLM black box | grep .tap.json — every op is a row |
| License of the plan format | Stagehand SDK is MIT but the format is whatever your TS code does | tap-v1 namespace (taprun.dev/ns/tap-v1), JSON Schema 2020-12, @taprun/spec MIT |
| Pricing model | Stagehand SDK free; Browserbase paid by browser-hour + actions | $0 spec & runtime free forever; AI compile $19/mo, AI heal $49/mo, team $199/mo |
| Backed by | Browserbase (funded company, $20M+ raised) | Bootstrapped, solo |
| GitHub stars | 22K | 3 |
| npm weekly downloads | 745K (@browserbasehq/stagehand) |
small (@taprun/spec published 2026-04-26) |
Distribution. 22K stars and 745K weekly npm downloads is real network effect. Tap is at the start of its ecosystem; Stagehand has been growing for two years. If you need a battle-tested community and immediate Stack Overflow answers, Stagehand is the safer bet.
Scale-out. Cloud-first means you can run thousands of headless browsers in parallel without buying machines. Tap’s local-first model puts the scaling burden on you.
Anonymous public scraping. If your task has no login state — say, scraping all GitHub trending repos — there’s nothing to protect locally. Stagehand+Browserbase is engineered for this; Tap’s privacy moat is irrelevant.
Stagehand’s act() ergonomics. Natural language directly to action is genuinely good UX for prototyping. Tap requires you to compile a plan first (via forge) — slightly more friction up front, more determinism after.
Vision-based reasoning. Stagehand can chain LLM-at-runtime with screenshots; Tap’s compile-time model is text/DOM only. If your task needs the model to look at the rendered page, Stagehand wins.
Polished docs + example gallery. docs.stagehand.dev is a serious investment. Tap’s documentation is improving but not on Stagehand’s level yet.
Local-first by architecture. Six independent engineering-philosophy facts (A2/A3/D2/R1/T1/R3) all align with local-first execution — see the local-first argument for the full derivation. Cloud-first SDKs cannot match this without a fundamental rewrite.
Plan-as-data. A .tap.json file is a data blob you can grep, version, audit, sign with APS receipts, or feed to a static analyzer. A Stagehand .ts file with embedded act() calls is opaque — you have to read it (or run it) to understand what it does.
Zero LLM tokens at runtime. A compiled .tap.json replays at $0 LLM cost, deterministically, every time. Stagehand’s caching reduces but doesn’t eliminate runtime LLM calls — every uncached path is a fresh inference.
Drift-aware verify. Tap’s verify independently fetches an authoritative source (JSON-LD / OpenAPI / sitemap / RSS) and cross-validates the scraped output against it. Stagehand’s “self-healing” re-runs the LLM; Tap’s verify notices that a name extraction now matches schema.org’s Product type 0% of the time.
Bring-your-own-script via adapters. Tap’s from-playwright, from-puppeteer, and from-stagehand packages convert existing source into .tap.json. You don’t have to rewrite to start using Tap; Stagehand requires migration to its SDK.
Vendor exit. If Browserbase shuts down or pivots, your Stagehand-based scrapers stop. If Tap-the-company shuts down, your .tap.json plans keep running on local CLI forever.
Compliance-friendly cohort. HIPAA/SOC2/legal-finance teams can use Tap because data and plans never leave the local boundary. Cloud-first SDKs are usually a non-starter for these cohorts.
Tap’s from-stagehand adapter is a deliberate choice: we’d rather you keep your Stagehand code and add Tap on top than ask you to rewrite.
If you have a Stagehand .ts script and want to add drift detection / audit trail / local fallback:
npm install @taprun/from-stagehand @taprun/spec
import { stagehandToTap } from "@taprun/from-stagehand";
import { runConformance } from "@taprun/spec";
const source = await readFile("scripts/my-stagehand-script.ts", "utf8");
const plan = stagehandToTap(source, {
site: "github",
name: "browserbase-search",
intent: "read",
});
// runs validation
const v = runConformance(plan);
if (!v.pass) throw new Error(JSON.stringify(v.failures));
// you now have a .tap.json that can run via Tap CLI alongside your Stagehand script
await writeFile("github/browserbase-search.tap.json", JSON.stringify(plan, null, 2));
Note:
from-stagehandis deprecated as of v2. Stagehand requires Browserbase (cloud-coupled), which contradicts the local-first stance. The recommended path is@taprun/from-playwrightagainst the deterministic page-level calls in your Stagehand script (Stagehand wraps Playwright anyway), then re-capture the natural-language portions viatap capture <url> <site>/<name> --intent "...". See the Migration guide.
The result is a partially deterministic plan. Tap’s verify can check the deterministic portions; the NL portions remain Stagehand’s job. Most production Stagehand scripts have a deterministic backbone with NL embellishments — Tap converts the backbone, and you keep using Stagehand for the NL parts.
You’re using Stagehand and want to try Tap on a single script:
# 1. Convert (one-shot)
npx -p @taprun/from-stagehand -e "
const fs = require('fs');
const { stagehandToTap } = require('@taprun/from-stagehand');
const src = fs.readFileSync(process.argv[1], 'utf8');
const plan = stagehandToTap(src, { site: 'mysite', name: 'flow', intent: 'read' });
fs.writeFileSync('flow.tap.json', JSON.stringify(plan, null, 2));
" scripts/your-stagehand-script.ts
# 2. Run via Tap CLI
brew install LeonTing1010/tap/taprun
tap run mysite/flow
# 3. Optional — install Chrome extension for logged-in sites
# Chrome Web Store: https://chromewebstore.google.com/detail/tap/llcidejeoobdegbkolbjhfoeckphldce
You’re starting fresh and unsure which to pick:
.tap.json actually looks likeLast updated 2026-04-27. Stagehand stats from @browserbasehq/stagehand on 2026-04-26.