That's Playwright's TodoMVC example, 24 tests, same machine, same npx playwright test. Left: 3.65 s. Right: 1.05 s.
I built Flashwright, a fork of Playwright that keeps its API intact while making it blazingly fast!
You can find below benchmarks for nine frameworks on one suite, 18 Playwright scenarios head to head and what I did to make it faster.
Swap it in
npm i -D @playwright/test@npm:flashwright-test
npx playwright install chromium
npx playwright test
Nine frameworks, one suite
| Tool | 1 worker | 4 workers |
|---|---|---|
| Flashwright | 2.41 s | 1.54 s |
| Bun test + Bun.WebView | 4.39 s | 2.80 s |
| Playwright | 4.63 s | 3.12 s |
| Selenium + Mocha | 5.48 s | 2.77 s |
| Puppeteer + Jest | 6.29 s | 2.98 s |
| Nightwatch | 6.45 s | 3.38 s |
| WebdriverIO | 10.57 s | 4.91 s |
| Cypress | 17.29 s | 17.37 s (one spec at a time without Cypress Cloud) |
| TestCafe | 22.55 s | 9.28 s |
TodoMVC is tiny, so this is mostly measuring the framework. On a slow app the gap could be a smaller.
Playwright's own benches
At least 2× everywhere, 2.86× on average. Whole test command, median of 15 alternating runs, headless Chromium, warm session off (CI mode). Test code untouched.
| Scenario | Workers | Playwright | Flashwright | Speedup |
|---|---|---|---|---|
| TodoMVC (24 tests) | 1 | 3670 ms | 1011 ms | 3.63× |
| TodoMVC | 4 | 1812 ms | 734 ms | 2.47× |
| Click + assertion (15 tests) | 1 | 1611 ms | 402 ms | 4.01× |
| Suite with real failures | 1 | 4334 ms | 1154 ms | 3.76× |
| Shop app (setup project, 40 ms API) | 1 | 6297 ms | 2967 ms | 2.12× |
Strip out app and server time and Flashwright spends 3–4.5× less than Playwright on framework overhead. Reproduce: node bench/matrix.mjs 15.
Where the time went
Most of a "fast" e2e test isn't your test. It's setup, waiting, and starting over.
Warm context, fresh tab. Playwright opens a new browser context per test, so V8 compiles your app again and again. We keep one context per worker and give each test a new tab, then reset cookies, storage, routes, permissions, downloads, clock, the lot, before the next test. Want Playwright isolation back? isolation: 'strict'.
Waits that listen. Auto-wait used 20–500 ms backoff. We re-check as soon as the page moves. If nothing on the page can still change the DOM (no timers, requests, navigation, animation, workers), failing assertions bail immediately. That failure suite: 4334 ms → 1154 ms.
Input without frame tax. Clicks and hovers don't wait for animations to finish. Hit-target checks still block clicks on the wrong element.
Start work before you need it. Workers and browsers spin up while the CLI reads config. Idle spares hold the context the next test wants. After a failure, a spare is already warm. Slow tests from last run go first when you've got parallelism.
Cheaper cold start. V8 code cache on our bundles, lazy load what this run doesn't need, fewer round trips per action, leaner page scripts.
Screenshots. Faster PNG encode (same pixels). toHaveScreenshot() baselines stay byte-identical to Playwright.
Designed for agentic workflows
Agents don't run the whole suite once. They edit, run one test, edit, run again. Outside CI we keep a warm session: runner, workers, and browsers stay up for five minutes after the last run, contexts reset, renderers warm.
| Run, 1.5 s after the previous | Playwright | Flashwright | + warm session |
|---|---|---|---|
| One TodoMVC test | 673 ms | 240 ms | 76 ms (8.9×) |
| TodoMVC suite, 4 workers | 1804 ms | 728 ms | 508 ms (3.6×) |
Fresh processes each run so config and env changes still land. FLASHWRIGHT_SESSION=0 kills it. CI keeps it off.
MCP: playwright mcp --adaptive-settle returns when the page is done, not after a fixed 500 ms. One save-through-API click: 1014 ms → 234 ms.
Dial it back to Playwright when you need to
Defaults favor speed. Per project, file, or describe:
export default defineConfig({
testOrder: 'file',
use: {
waitForAnimations: true,
failFastWhenIdle: false,
},
});
test.use({
instantAnimations: true,
blockResources: ['image', 'font', 'media'],
});
More knobs (isolation: 'strict', workersPerProcess, locatorPolicy: 'test-id', etc.) and measured effects are in the README.
Compatibility
I ran it against Playwright 1.64.1 on the same box: tests/page 2616/2616 on Chromium, playwright-test / mcp / electron / extension parity, library parity except two internal bookkeeping tests that see recycled contexts, stress 1100/1100 in 20 s vs 49 s.
Links
- Repo and benches: https://github.com/xcodebeast/flashwright
- https://flashwright.dev/
- npm:
flashwright-test,flashwright,flashwright-core
If you throw it at a real suite, tell me the before/after. Especially anything that diverges from Playwright. GitHub issues are open.



Top comments (0)