A manual Playwright alternative where the video is watchable
Every other page about this topic swaps the library. Cypress instead of Playwright. Selenium instead of Playwright. A $7,500 per month SaaS instead of both. None of them touch the part that actually makes a manual Playwright suite painful to live with: the recording of a test run has no cursor in it, so the bug evidence never leaves engineering. Assrt keeps Playwright and fixes the video. The file is browser.ts. The function is 66 lines long.
The alternatives every other guide recommends, and why none of them fix the recording
Look at the five pages that currently answer this topic. Each one lists the same options. Cypress. Selenium. Puppeteer. TestCafe. WebDriverIO. Some also recommend a proprietary AI SaaS at $2,500 to $7,500 per month. The rationale is always the same shape: Playwright is a library, these other things are also libraries, therefore switching is an alternative.
The rationale misses the reason people look for a manual Playwright alternative in the first place. The hand-written code works. The CI runner works. Chromium launches, the test passes, the PR is green. The problem is what happens after that green check, when a support engineer in a different time zone opens a ticket that says the checkout is broken. Your suite has a test for the checkout. The test passed two hours ago. You hand them the Playwright video. They watch a white page click itself. No cursor. No indication of what got typed. No visible evidence that a human-equivalent action happened at the exact place the bug report points to.
Every alternative ranked for this topic has the same problem, because the problem is not which library you chose. The problem is that headless Chromium does not render a pointer, and nothing in a test library adds one. Replacing Playwright with Cypress does not add a cursor. Replacing Cypress with Selenium does not either. A manual Playwright alternative that is worth switching to has to change the artifact, not the library.
The file that defines the alternative
Open /Users/matthewdi/assrt-mcp/src/core/browser.ts and scroll to line 33. A 66-line template literal called CURSOR_INJECT_SCRIPT attaches four DOM elements to every page. It is what turns the invisible Playwright run into a piece of video you can paste into Slack without annotation.
None of these elements participate in page layout. They use position: fixed, sit at the max 32-bit signed z-index (2147483647), and set pointer-events: none. The application under test cannot tell they are there, but the CDP screencast captures them because they render through the normal compositor pipeline. The four IDs are namespaced with the __pias_ prefix so they cannot collide with an id an application engineer might have picked.
The four elements, one by one
Every overlay element exists for a reason. Three of them are visible artifacts of the action; the fourth is a CDP trick. All four are necessary for the replay to feel like a human.
__pias_cursor
A 20px red dot with a 2px white border and a red shadow. Animated via a CSS transition so it glides between targets in 0.3s ease instead of teleporting. Server-side position (cursorX, cursorY on the manager) persists across navigations and SPA route changes.
__pias_ripple
A 40px ring that triggers on every click. Scales from 0.5 to 2 and fades to opacity 0 over a 50ms setTimeout + CSS transition cycle. The pattern viewers recognize as 'something was clicked here.'
__pias_toast
Black pill at the bottom of the viewport. Reads '⌨ typing: "..."' with the first 40 characters of whatever was typed (browser.ts line 578). Opacity drops after 2.5 seconds so the toast does not pile up.
__pias_heartbeat
A 6px green dot in the bottom-right corner. Animates opacity 0.2 to 0.8 and scale 0.8 to 1.2 on an 800ms Infinity iteration. Its only job is to force the CDP compositor to emit frames on idle pages so the recording does not stall.
injectOverlay() is idempotent
Every navigate(), click(), type(), and selectOption() calls injectOverlay first. The script no-ops on re-entry because of the window.__pias_cursor_injected flag; on a full navigation the flag resets, so the overlay reattaches automatically.
Nothing leaks into the DOM hash
All four IDs use the __pias_ prefix. The elements use pointer-events: none and position: fixed, so they never intercept clicks and never shift layout. The AUT cannot detect them and never needs to.
How an action becomes a frame in the video
The 400ms that makes the motion land
Injecting a cursor is not enough. You also have to give the recording time to capture it moving. That is what the setTimeout(400) in the click handler does. Every click and every type starts with a call to showClickAt, which computes the target center and fires window.__pias_showClick. The cursor starts gliding. Then the runner sleeps for 400ms to let the 0.3s CSS transition finish. Only then does the real Playwright call fire.
On a 20-step scenario that is about 8 seconds of added wall time. On a suite that is going to be watched by a non-engineer, that 8 seconds is the difference between an artifact you can share and a trace file only a developer can interpret.
“Default Playwright video recording captures zero cursor information. Playwright's own docs recommend the Trace Viewer for this, which requires a local test checkout to open. The Assrt overlay paints the cursor into the video itself, so the file is self-contained.”
Playwright docs on videos + Trace Viewer, April 2026
Same flow, two recordings
Here is the exact same login flow in both styles. The manual Playwright test on the left is real, runnable code. The Assrt plan on the right is real, runnable plain text. The difference in the resulting video is the whole point of this page.
Run the .spec.ts on the left. Open the resulting .webm. The page navigates. Form fields fill in. The submit button transitions to a new route. At no point does the viewer see a cursor, a click gesture, or what text was typed. Shared out of engineering, this artifact looks like a page that navigates itself for no reason.
- No cursor rendered anywhere in the recording
- Typed text appears in the field but no indication of what was typed when
- Click events are invisible beyond their consequence
- Non-engineers cannot review the run without engineer narration
The two files, side by side
On the left: the hand-written Playwright test. On the right: the Assrt plan that runs the same flow, produces the same green/red pass result, and additionally ships a recording a PM can read.
What a real run looks like in the terminal
The MCP server logs every tool call including the overlay injection. Here is a condensed transcript of a five-step scenario, from launching Playwright through to the video landing on disk.
Against the alternatives that currently rank for this topic
The shortlist of tools pitched as “the manual Playwright alternative” in 2026, judged on the axis that actually matters once you have a test running: can a non-engineer watch the result and understand what happened?
| Feature | Cypress / Selenium / QA SaaS | Assrt |
|---|---|---|
| Browser engine | Cypress: Electron; Selenium: WebDriver; QA SaaS: proprietary | Playwright (via @playwright/mcp, same Chromium) |
| Cursor in the recording | None. Headless engines paint no pointer. | Red dot glides via CSS transition before every click |
| Typed input in the recording | Shows up in the field only, no temporal indication | Toast reads first 40 chars of every typed string |
| Click confirmation | Inferred from page transition | 40px ripple expands on the exact click coordinate |
| Idle-page compositor frames | CDP can stall; recording freezes | Heartbeat pulse forces continuous frames |
| Test format | TypeScript/JavaScript .spec files with selectors | Plain English #Case markdown, agent-interpreted |
| Artifact shareable with a PM | Needs engineer to narrate Trace Viewer | Paste the URL. Recipient watches a human-style demo. |
| Price | Free libraries + engineer time; or $2.5K–$7.5K/mo SaaS | MIT, pennies per run for LLM calls |
| Source code of the replay layer | Not applicable (no replay layer) or vendor-closed | 66 lines in browser.ts, MIT, fork and change the color |
The workflow this unlocks for small teams
A manual Playwright suite that only engineers can review gates every bug report behind engineering. Assrt's overlay pushes that gate open, because the artifact stops needing a translator.
Support files a bug
A ticket comes in saying the checkout flow rejects a valid card. The reporter saw it in production but cannot reproduce it locally.
Engineer runs assrt_test
Claude Code, or a teammate at the CLI, invokes assrt_test against the staging URL with a scenario that covers the exact flow. The run takes about a minute. The video is written to /tmp/assrt/<runId>/video/recording.webm.
The link goes to Slack
The MCP response contains a central-API URL like https://app.assrt.ai/s/<uuid>. The engineer pastes it in the ticket channel. No export, no file attach, no trace viewer.
Product watches the video
The red cursor glides to the card field, the toast reads the card number as it types, the ripple fires on Submit, and the error banner renders. The product manager understands the repro without reading the .spec file.
Scenario becomes a regression test
Same UUID. Same plan text. Runs in CI on every deploy. Every subsequent run drops a new video into the same scenario page, so the next bug review starts with historical context already present.
A correction, stated plainly
Assrt is not faster than hand-written Playwright. A tight .spec.ts runs in 5 to 30 seconds. An Assrt scenario averages around 60 seconds because the agent inference loop and the 400ms cursor-glide both cost wall time. If the objective is a sub-second smoke test gating CI, Assrt is not the answer.
Assrt is faster to author, and the artifact is faster to triage. A 50-case suite that takes a week to write in Playwright typically takes an afternoon in plain text. And every run produces a video a non-engineer can review in the same time it takes to watch it, with no trace-viewer import step and no source-code context needed.
The trade, stated plainly: you give up 30 to 40 seconds per scenario of execution wall time. You get back all of the authoring time, all of the selector-maintenance time, and the ability to hand bug evidence to anyone who can click a link.
How much of the overlay code you can change yourself
The package is MIT. The overlay is literally a TypeScript string constant. Every visual decision in the replay is a line you can edit.
Want a blue cursor? 0 line: change the rgba on background: 'rgba(239,68,68,0.85)' at browser.ts:54.
Want a bigger ripple? 0 lines: change the 40px width and height on browser.ts:64.
Want longer keystroke toasts? Change the slice from 40 to 80 at browser.ts:578. That is 0 line.
Want the overlay disabled entirely for a speed-first run? Delete the await this.injectOverlay() call in navigate, click, type, and selectOption. 0 lines.
You do not negotiate this with a vendor. You do not request a feature flag. You open browser.ts, edit the line, and rebuild.
Get the cursor-painted video for your own app
Thirty minutes, on your URL: we run assrt_test against a flow you pick, share the webm link, and step through the four DOM elements that make it watchable.
Book a call →Frequently asked questions
What does 'manual Playwright alternative' usually mean, and why is Assrt a different answer?
Most articles on this topic recommend swapping the library: Cypress instead of Playwright, Selenium instead of Playwright, Puppeteer, TestCafe, WebDriverIO. Assrt does not swap the library. The browser engine is still Playwright (we call @playwright/mcp under the hood, see /Users/matthewdi/assrt-mcp/src/core/browser.ts lines 274-377). What Assrt replaces is two things that have nothing to do with the library: the authoring layer (you write plain-English scenarios instead of .spec.ts files) and the replay layer (every action is painted onto the page as a visible cursor and toast so the Playwright video is watchable by someone who has never seen the code).
Why does replay watchability matter if Playwright already records a video?
Playwright records what the headless Chromium shows, and headless Chromium has no real pointer. The default video captures page transitions and DOM changes, but not where the test clicked, what keys it pressed, or how long it waited. A developer can read it against the .spec.ts file or load it into the Trace Viewer. A product manager, designer, or support engineer cannot. That is why bug evidence from a manual Playwright suite typically stays inside engineering. Assrt fixes this at the source: a 66-line script called CURSOR_INJECT_SCRIPT (browser.ts lines 33-98) attaches four DOM elements to every page at z-index 2147483647, so the compositor paints them into the exact same video Playwright was already recording. The artifact becomes a shareable link instead of a trace you have to translate.
What exactly gets painted on the page during a run?
Four elements. __pias_cursor is a 20px red dot with a 2px white border, positioned via fixed left/top and animated with a 0.3s ease CSS transition so it glides between targets instead of snapping. __pias_ripple is a 40px ring that expands from scale 0.5 to scale 2 and fades out over a 50ms + transition cycle, triggered on every click. __pias_toast is a black-pill label at the bottom of the viewport that reads '⌨ typing: "..."' with the first 40 characters of whatever was typed (browser.ts line 578). __pias_heartbeat is a 6px green dot in the bottom-right corner that alternates opacity and scale on an 800ms Infinity-iteration animation; it exists not as decoration but to force CDP to emit continuous compositor frames even on pages that would otherwise pause repainting.
How does the cursor actually get to the right element?
Before every click, Assrt calls showClickAt (browser.ts lines 451-501). That method runs browser_evaluate in the page, tries document.querySelector(sel) first, and if that fails walks every anchor, button, input, [role=button], select, textarea, label, [onclick], and [href] on the page and scores them by text match against the target string (exact match wins, substring match scores 3, reverse-substring scores 2, word overlap scores fractionally). It reads the best candidate's bounding rect, computes the center, and calls window.__pias_showClick(x, y). The cursor glides to that center via the CSS transition. The runner then sleeps for 400ms (browser.ts line 568), and only then does the real browser_click tool fire against Playwright. The motion lands in the video because the recording captures those 400ms of transition before the click happens.
Does this slow the test down? How much?
It adds roughly 400ms of wait per click and per type (the cursor-glide settle time) plus one small browser_evaluate per interaction for the overlay injection. On a scenario with 20 actions, that is about 8 extra seconds. The real bottleneck on an Assrt run is the LLM decision loop, not the overlay: typical end-to-end scenario duration is around 60 seconds (agent inference plus navigation plus the 400ms glides). A hand-written Playwright test of the same flow runs in 5 to 30 seconds with no visible cursor. If pure wall-clock speed is the priority, keep manual Playwright. If the video needs to be watchable by a non-engineer, the 400ms per click is the price.
Can I run this against my own app right now without signing up for anything?
Yes. Install with npx assrt-mcp and add it as an MCP server to Claude Code, Cursor, or Claude Desktop. Point your coding agent at a URL and ask it to run assrt_test with a plan written like '#Case 1: Sign in works. Go to /login. Type x into email. Click Sign in. Assert dashboard heading appears.' The video, including the cursor, ripple, and keystroke toast, ends up at /tmp/assrt/<runId>/video/recording.webm on your disk. The scenario is stored by UUID at the central API and mirrored to ~/.assrt/scenarios/<uuid>.json, so you can share the link without signup on the recipient side. There is no login flow on the public data plane.
Does the overlay survive page navigation? What about single-page app route changes?
Yes to both. The injection is idempotent: the script starts with `if (!window.__pias_cursor_injected) { window.__pias_cursor_injected = true; ... }` (browser.ts line 34), and injectOverlay is called after every navigate (browser.ts line 518) and before every click or type. On a full-page navigation the DOM is rebuilt, so the flag is cleared and the overlay is reattached. On an SPA route change the flag stays set, so the script no-ops and the existing DOM elements are reused. The cursor position is tracked server-side (cursorX, cursorY on the manager) and restored without animation immediately after re-injection, so it never pops back to off-screen.
How is this different from Playwright's built-in Trace Viewer?
The Trace Viewer is an engineer tool: you open a .zip, scrub a DOM timeline, and inspect network activity and console logs against the source code of the test. It answers 'why did my assertion fail?' for someone who wrote the test. Assrt's overlay plus video answers 'what did the run actually look like?' for someone who did not write the test. They solve different problems. Trace Viewer needs a local checkout of your test suite to be useful; the Assrt artifact is a webm the recipient watches in any browser. A team can and should use both. But if the goal is to send bug evidence to product, QA, or a client, the trace viewer does not work and the overlay-painted video does.
Is the overlay open source? Can I fork or disable it?
The MCP package @assrt-ai/assrt is MIT licensed. CURSOR_INJECT_SCRIPT is a template literal constant exported from the same module as the McpBrowserManager class. If you want to change the cursor color from red to blue, edit the rgba values on line 54. If you want bigger click ripples, change the width/height on line 64. If you want the keystroke toast to show 80 characters instead of 40, change the slice length on line 578. To disable the overlay entirely for a speed-first run, comment out the injectOverlay calls in navigate, click, type, and selectOption. There is no feature flag wall or plan gate; the overlay is literally eight function calls in browser.ts.