open, click @e2, fill @e3 "text", screenshot, close. By default it drives
a new browser (Chrome for Testing, or a detected system Chrome/Brave) with a fixed command set.execute. It runs any Playwright code in your already running
Chrome with your logins and extensions. The agent writes real JavaScript with loops, conditions,
and variables. It already knows Playwright, so there is nothing new to learn.| Playwriter | agent-browser | |
| Browser | Your running Chrome (extension) | New browser by default |
| Login state | Already logged in | Fresh (or a read-only profile snapshot) |
| Extensions | Your existing ones | None by default (--extension <path> for unpacked) |
| API surface | 1 execute tool + full Playwright | 50+ CLI commands, one per action |
| Multiple actions per turn | Real JS: loops, conditions, variables | batch of quoted command strings |
| Reusable logic | Import a .js module with the live page | Shell scripts or batch command lists |
| LLM knowledge | Already knows Playwright | Must learn 50+ custom commands |
| Bot handling | Real Chrome; disconnect for manual challenges | Automated browser by default; stealth via plugin |
| Video recording | Native tab capture, no base64 | CDP screencast piped to ffmpeg |
| Skill recorder | Record your workflow → reusable skill | Bundled reference skills only |
| Cloud browsers | Built-in stealth + proxy + CAPTCHA | Provider plugin |
| Remote tab sharing | Built-in one-click secret tunnel | No built-in tab-sharing tunnel |
| Raw CDP / debugger / live edit | Full, first-class | Via eval / CDP url |
find role button click, get attr,
storage local set, network route, and dozens more. Its optional MCP mode loads tool schemas
into client context (mitigated with tool profiles and paginated discovery). When you need something
the command set does not cover, you fall back to eval or raw CDP.execute tool. The agent writes Playwright, which it already knows from
training. There is no command vocabulary to memorize and no per-tool schema tax.# agent-browser: separate commands, each a fixed shape agent-browser open example.com agent-browser snapshot agent-browser click @e2 # Playwriter: one call, real Playwright the model already knows playwriter -s 1 -e 'await page.goto("https://example.com"); await page.getByRole("button", { name: "Submit" }).click()'
batch array of quoted command strings. batch --bail
stops on the first error and returns structured results, but there is still no real control flow,
no variables, and no branching between steps.# Playwriter: loop, branch, and collect in a single execute playwriter -s 1 -e ' const rows = await page.locator("table tr").all(); state.results = []; for (const row of rows) { const name = await row.locator("td.name").textContent(); if (name?.startsWith("A")) { await row.locator("button.expand").click(); state.results.push(name.trim()); } } console.log(state.results); '
eval command, but it runs in-page JavaScript (document, window), not
the Playwright driver. You cannot mix locator actions, auto-waiting, and Node-side state in one
program the way you can with Playwriter..js file, replay forever// submit.js export async function submitProduct({ page, name, url }) { await page.goto('https://directory.example.com/submit') await page.getByRole('textbox', { name: 'Product name' }).fill(name) await page.getByRole('textbox', { name: 'Website URL' }).fill(url) await page.getByRole('button', { name: 'Submit' }).click() await page.waitForResponse(r => r.url().includes('/api/products')) }
# Replay any time, from any session playwriter -s 1 -e 'const { submitProduct } = await import("./submit.js"); await submitProduct({ page, name: "Acme", url: "https://acme.com" })'
page object and
runs driver-side control flow (locators, auto-waiting, waitForResponse) in the same program.--profile <name>, use a
persistent writable profile path, load unpacked extensions with --extension, or attach to a Chrome
you launched with --remote-debugging-port (which shows Chrome's permission dialog an agent cannot
dismiss).chrome.tabCapture inside the extension. The MediaRecorder lives in the
browser, so recording is native 30-60fps, survives page navigation, and never ships base64
frames over the wire.playwriter -s 1 -e "await recording.start({ page, outputPath: './demo.mp4', frameRate: 60 })" playwriter -s 1 -e "await page.click('a'); await page.waitForLoadState('domcontentloaded')" playwriter -s 1 -e "await recording.stop({ page })"
skills get <name>) that always match the installed
CLI version. Playwriter has a Skill Recorder for your own flows: perform a workflow once by hand
in your real browser, and the agent turns the recording into a reusable skill with verified
locators, expected network outcomes, and an importable replay script. Show a flow instead of
describing it. See Skill Recorder.playwriter session new, so your scripts work unchanged. See
Cloud Browsers.state): build up context, reuse pages and handles.createDebugger.createEditor.page.route, request/response listeners, mocking, HAR.aria-ref locators, plus Vimium-style visual labels.