Run hundreds of Chrome jobs on one machine.

Fluxwright pools Chrome, gives every job a fresh isolated page, queues work when the fleet is full, restarts browsers before they bloat, and retries when one crashes. Use it from Node, from Rust, or from an AI agent over MCP.

npm install fluxwright

Version 0.2.1, open source under Apache-2.0. Prebuilt for Windows, macOS and Linux.

Simulated fleet

Jobs done
1,284
Browsers recycled
3
Crashes retried
1
Each row is one Chrome process with eight page slots. A job leases a free slot on the least busy browser; when every slot is taken it waits in the queue. A browser that grows past 900 MB takes no new jobs, finishes the ones it has, and restarts. An illustration, not measured data.

What it takes care of

Libraries like Playwright and Puppeteer drive one browser well. Running many at once needs a control plane: how many Chromes, when to refuse work, when to restart one, what to do when a renderer dies. That is Fluxwright.

A fresh page per job
Every job gets its own browser context on a pooled Chrome. Cookies, storage and cache never leak into the next job.
browser.newPage()
A queue with priorities
When every slot is busy, jobs wait in a priority queue, or fail fast if you would rather shed load.
QueueFullMode::Wait
A memory ceiling
No new work starts while the fleet's real memory footprint is over your budget. Shared pages are not counted twice.
memory_ceiling_mb
Recycling
A browser restarts after a number of jobs, an age, or a memory threshold. It drains its running jobs first.
recycle_after_jobs
Crash recovery
A dead browser fails its jobs at once and they retry on a healthy one. Chrome is tied to the engine, so it never outlives a crash.
JobOptions::retries
A Playwright-style API
getByRole, getByLabel, getByTestId, nth and filter, frameLocator for cross-origin iframes, page.route to fake or change requests, downloads, and clicks that wait until the element can take them.
page.getByRole()
Settings per job
Each job can have its own proxy, user agent, locale, timezone and location, and start from a saved login, on the same browser as jobs that use none.
newPage({ storageState })
Built for agents
An MCP server lets Claude, Codex or Cursor drive the fleet on your machine.
fluxwright-mcp

How a job runs

The same five steps whether a job comes from Node, Rust or an agent.

  1. Submit

    Your code asks for a page with browser.newPage(), or hands a job to engine.run().

  2. Admit

    If every slot is busy or memory is over the ceiling, the job waits in the queue by priority.

  3. Lease

    The job gets a fresh context on the least busy Chrome. Nothing is shared with other jobs.

  4. Run

    Your code drives the page: navigation, locators, evaluate, screenshots.

  5. Release

    Closing the page destroys the context. A browser that reached its limits drains and restarts.

The code

A small API on purpose: pages you lease, locators that wait, and an engine that handles the fleet. Node gets a native addon, so there is no driver process in between.

The MCP server exposes open, goto, click, fill, evaluate, screenshot and close. Selectors accept CSS, text= and role=.

import { chromium } from "fluxwright";

const browser = await chromium.launch({ maxBrowsers: 4 });

// A fresh browser context on a pooled Chrome.
const page = await browser.newPage();
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
console.log(await page.title());

// Locators wait until the element can be used.
await page.getByRole("button", { name: "Accept" }).click();
await page.getByRole("textbox", { name: "Search" }).fill("chromium");

// Cross-origin iframes work the same way.
const payment = page.frameLocator("iframe#payment");
const card = payment.getByRole("textbox", { name: "Card number" });
await card.fill("4242 4242 4242 4242");

await page.close(); // the context is destroyed and the slot is free
await browser.close();

Benchmarks

Jobs per second, higher is better. Both tools ran on the same chrome-headless-shell binary, alternating runs, with 10 jobs in flight loading a small local page.

Throughput in jobs per second
SetupFluxwrightPlaywright
5 browsers, 2 pages each66–7045–51
1 browser, 10 pages54–5842–46

One Windows machine, 30 September 2026. The first Fluxwright run of each series ran cold and slower (30 jobs/s) and is left out of the ranges. On Chrome's regular headless mode the two were level, at about 13 jobs/s. One machine and one page shape say little about yours, so measure your own workload with cargo run --release -p fluxwright-benchmarks.

Install

You need Chrome or Chromium on the machine. Headless runs use chrome-headless-shell when it is installed, which opened pages 5 to 10 times faster in our runs.

npx @puppeteer/browsers install chrome-headless-shell@stable
npm install fluxwright

Prebuilt for Windows x64, macOS on Apple silicon and Intel, and Linux x64 (glibc). Node 18 or later.

Know before you choose it

What it does not do

  • Firefox or WebKit. Fluxwright is Chromium only.
  • Test running: no fixtures, assertions, tracing, HAR or video. Bring your own test framework.
  • More than one page per context, downloads and uploads.
  • locator.filter, nth and getByLabel. getByRole, getByText and frameLocator are supported.
  • Attaching to a browser that is already running. Planned.

Safe by default

  • Chrome's sandbox stays on unless you set FLUXWRIGHT_NO_SANDBOX=1.
  • On Linux and macOS the engine talks to Chrome over a private pipe, not a debugging port.
  • Jobs never share cookies or storage.
  • Chrome exits when the engine exits, even after a crash.