Lightpanda is a browser written from scratch in Zig, with no Chromium and no rendering engine, aimed at the one workload that grew fastest in 2026: agents reading the web. It left beta on 2 October 2026 with version 1.0, and on 2 November 2026 the repository shows 40,121 stars. Its README opens with a two-line claim — “16x lighter and 9x faster than Chromium” — and a table.
I downloaded the nightly build, ran it against ten real pages alongside Playwright’s Chromium, connected to its CDP server with Playwright, and then parsed the raw JSON behind its public Web Platform Tests dashboard instead of reading the pass counter. Three things came out of that work that the README does not say.
First, the 1.84 million WPT passes are mostly one suite: encoding/ contributes 1,151,555 of them, and wasm another 191,425 — together 72.9% of the total. Second, “9x faster” is a concurrency-scaling result, not a per-page one: at equal concurrency the gap is 1.6x, and the 9x appears only when you compare 25 Lightpanda OS processes against 25 Chrome tabs. Third, and most practically, Lightpanda serves one page per browser process: the standard Playwright pattern of opening a second page in the same context fails with a protocol error, and a second browser context cannot be created at all.
The numbers first
| Fact | Value | Source |
|---|---|---|
| Stars / forks / watchers | 40,121 / 1,962 / 98 | GitHub API, 2026-11-02 00:46 UTC |
| Watchers as share of stars | 0.24% | computed |
| Contributors / commits | 84 / 11,486 | GitHub API contributors |
| Top three contributors | 4,991, 2,227, 1,677 commits (77.4% of all commits) | GitHub API |
| Contributors with ≤2 commits | 41 of 84 | computed |
| Created / last push | 2023-02-07 / 2026-11-02 00:28 UTC | GitHub API |
| License | AGPL-3.0 | LICENSE |
| Code by bytes | Zig 11.3 MB, HTML 3.1 MB, Rust 144 KB | GitHub API languages |
| Releases / tags | 1.0.2 (2026-10-23), 1.0.0 (2026-10-02) / 27 tags | GitHub API |
| Open issues | 99 | GitHub API |
| Binary I ran | 1.1.0-nightly.11471+cebb8d51e, 77 MB | lightpanda version |
The watcher count is the number I would look at first. Stars are a bookmark; watchers are people who asked to be told when something changes. 98 watchers on 40,121 stars is a 0.24% ratio — for a project that publishes daily WPT runs and shipped 27 tags, that is a crowd watching the idea more than the code. The fork ratio is 4.9%, which is normal for infrastructure that people self-host; a browser that runs as a service is something you clone, not something you star and forget.
The commit distribution is healthier than most: 84 contributors, but the top three account for 77.4% of 11,486 commits, and karlseguin alone has 4,991. This is a small core with a long tail of one-off fixes — the shape of a project that is being built, not one that is being maintained.
What “9x faster” is actually measuring
The README quotes two numbers: 123 MB against 2 GB of peak memory (~16x) and 5 s against 46 s of execution (~9x). The benchmark document that produces them crawls 933 URLs of the project’s own Amiibo demo site with a Go chromedp crawler. Here are the rows the headline is drawn from, plus the row that matters most:
| Run | Duration | Memory peak | % CPU | Pages |
|---|---|---|---|---|
| Lightpanda, 1 process | 0:51.68 | 27.2 M | 11.1% | 933 |
| Lightpanda, 25 processes | 0:04.81 | 123.0 M | 204.7% | 933 |
| Lightpanda, 100 processes | 0:05.23 | 410.2 M | 202.8% | 933 |
| Chrome, 1 tab | 1:22.83 | 1.3 G | 124.9% | 933 |
| Chrome, 10 tabs | 0:45.62 | 1.7 G | 241.6% | 933 |
| Chrome, 25 tabs | 0:46.70 | 2.0 G | 254% | 933 |
| Chrome, 100 tabs | 1:09:37 | 4.2 G | 229% | 933 |
The 123 MB / 5 s versus 2 GB / 46 s pair is the 25-versus-25 row. That is a fair comparison of throughput per unit of hardware, and it is the comparison a crawler operator cares about. But it is not a comparison of browser engines, because the two sides are not doing the same thing:
“Lightpanda can’t create multi-tabs. So instead we start 100 browser process, each on it’s own port.”
That sentence is in the benchmark document and not in the README table. It changes the interpretation of every row: 25 Lightpanda OS processes are being compared against 25 tabs inside one Chrome process. Part of the 9x is the engine, and part of it is that Chrome was made to multiplex 25 pages through one address space while Lightpanda was allowed to fork.
At equal concurrency — one process against one tab — the same table gives 51.68 s versus 82.83 s (1.6x) and 27.2 MB versus 1.3 GB (47x). The memory ratio at concurrency 1 is nearly three times better than the headline, and the speed ratio is nearly six times worse. Both numbers are “real”; they answer different questions.
There is a third observation in the Chrome column that nobody quotes. Chrome’s crawl time is 1:22.83 at one tab and 0:45.62 at ten tabs, and then it stops improving: 0:45.66 at five, 0:46.70 at twenty-five. Whatever bounds that crawl from ten tabs upward — network round-trips, the demo site, the crawler — it is not Chrome’s per-page cost. Lightpanda keeps improving all the way to twenty-five processes (0:04.81). The headline ratio is therefore mostly a statement about how cheaply Lightpanda can add concurrency, which is a genuine advantage — just not the one the words “9x faster than Chromium” imply.
Three inconsistencies between the two pages that publish the benchmark
These are small, and together they make the headline hard to reproduce:
| Claim | README | BENCHMARKS.md |
|---|---|---|
| Machine | “AWS EC2 m5.large instance” | “AWS m5.xlarge (x86_64) with a fresh Ubuntu install” |
| Workload | “Memory (peak, 100 pages)” / “Execution time (100 pages)” | both rows are labelled 933 pages |
| Chrome 100 tabs | — | 1:09:37 in the table, while the /usr/bin/time -v output printed directly above it reads Elapsed … 1:09.37 |
m5.large is 2 vCPU / 8 GB; m5.xlarge is 4 vCPU / 16 GB. The table’s own CPU column shows 204.7% and 254% utilisation at 25-way concurrency, which does not fit on a 2-vCPU machine — so the benchmark was almost certainly run on the larger instance and the README’s m5.large is stale. And 1:09:37 is a m:ss.ss value with its decimal separator turned into a colon: read literally it says Chrome needed 1 hour 9 minutes, which would be 83x its own 25-tab row. The real number is 69.4 s.
If you are sizing infrastructure off the README, the “100 pages” label is the one that will hurt: the rows behind it crawled 933 pages, so per-page memory is roughly 0.13 MB in the real run, not the 1.23 MB the README label implies.
Ten real pages, measured on this machine
I ran the nightly binary on ten pages that represent what an agent actually reads — static HTML, a server-rendered forum, a Next.js docs site, GitHub, Wikipedia, Python’s own homepage — with lightpanda fetch --dump markdown, and the same ten through Chromium via Playwright with waitUntil: networkidle and a 45 s cap. Sequential, single page at a time, on a 2-vCPU box with 7 GB of RAM.
| URL | Lightpanda | Chromium | Chromium nodes |
|---|---|---|---|
| example.com | 0.118 s | 0.569 s | 22 |
| news.ycombinator.com | 0.378 s | 0.929 s | 818 |
| news.ycombinator.com/item (538 comments) | 0.757 s | 1.293 s | 17,911 |
| lightpanda.io | 0.676 s | 1.523 s | 890 |
| demo-browser.lightpanda.io/amiibo | 0.184 s | 0.958 s | 39 |
| github.com/trending | 1.427 s | 2.793 s | 3,588 |
| react.dev | 0.555 s | 1.196 s | 1,846 |
| python.org | 0.531 s | 1.007 s | 902 |
| docs.python.org asyncio-task | 0.425 s | 0.798 s | 4,187 |
| en.wikipedia.org Headless_browser | 0.489 s | 0.983 s | 1,322 |
| total | 5.54 s | 12.75 s | — |
| peak RSS | ~70.5 MB (single process) | 599.6 MB (process tree) | — |
That is 2.3x faster and 8.5x lighter on this workload — real advantages, well short of 16x and 9x, and not a contradiction of the benchmark either, since I used concurrency 1 against concurrency 1 and a mixed page set instead of one demo site. The caveats are mine: the two sets ran about fifteen minutes apart, so network conditions moved; I did not throttle either side; and Lightpanda paid process startup ten times because each fetch is a new process.
Both sides succeeded on all ten URLs with exit code 0. Fidelity is the part that needs a second look, and it has its own section below.
Reading the raw WPT data instead of the pass counter
The dashboard at perf.lightpanda.io/wpt shows PASS: 1841066, FAIL: 283283, CRASH: 0 for the run of 1 November 2026 04:51 UTC, against commit cebb8d51e — the exact commit of the binary I downloaded. The page only renders a few dozen path rows, but the browser fetches a 218 MB JSON file behind it, and that file has every subtest. I aggregated it.
The totals reconcile exactly: 1,841,066 passing out of 2,124,349 subtests, 86.66%.
Now the composition, which is the part that changes how you read the headline:
| Path | Passing subtests | Total | Pass rate | Share of all passes |
|---|---|---|---|---|
encoding | 1,151,555 | 1,152,031 | 100.0% | 62.5% |
wasm | 191,425 | 191,957 | 99.7% | 10.4% |
html | 129,360 | 155,081 | 83.4% | 7.0% |
editing | 68,762 | 114,840 | 59.9% | 3.7% |
dom | 62,040 | 65,865 | 94.2% | 3.4% |
css | 56,597 | 177,275 | 31.9% | 3.1% |
webcryptoapi | 55,523 | 70,664 | 78.6% | 3.0% |
encoding/ is the Web Platform Tests suite for character-encoding lookup tables — tens of thousands of generated assertions of the form “byte 0x82 in Shift_JIS maps to this code point”. The project’s own 1.0 post is candid about this, and it is the single most useful paragraph in its release notes:
“April 2026: the encoding/ suite started passing. It holds about 1.15 million subtests, mostly large generated tables of character mappings, so the count jumped in a few days.”
So the jump from ~290,000 to ~1.7 million passes was one suite. Remove encoding and wasm and you have 498,086 passing subtests — 27% of the total, and the number that actually corresponds to “browser features a page can call”. For context, the 1.0 post puts Chrome at 2,184,491 subtests on wpt.fyi, so Lightpanda’s 1,841,066 is 84.3% of Chrome’s count — but the composition is nothing like Chrome’s, where css alone is a large share of pass and fail alike.
Two more facts that the single pass/fail counter hides:
- Only 11,626 of 38,199 test files (30.4%) pass completely. Seven in ten WPT files have at least one failing subtest. A pass counter dominated by two huge suites will not show this.
cssaccounts for 120,656 of the 283,283 failures — 42.6% of every failing subtest. That is the direct cost of having no layout or paint engine, and the project says so: “More than half of the subtests Chrome passes and Lightpanda doesn’t are in the CSS, editing and SVG suites, which test layout and painting.”
The suites that decide whether a given page will work are smaller and much more informative than the total:
| Suite | Pass rate | What breaks when it fails |
|---|---|---|
dom | 94.2% | Element manipulation |
cors | 90.9% (421/463) | Cross-origin fetch — was 371/463 in the 1.0 post |
cookies | 88.0% | Sessions |
indexeddb | 88.3% | Client storage |
html | 83.4% | Parsing, forms, media elements |
xhr | 79.1% | Older Ajax |
fetch | 70.0% | Modern Ajax |
websockets | 69.3% | Live sockets |
workers | 67.2% | Anything running in a worker |
content-security-policy | 46.1% | Pages that set CSP |
intersection-observer | 40.1% | Lazy-loaded content |
service-workers | 38.9% | Offline-capable apps |
streams | 32.0% | Streaming response bodies |
clipboard-apis | 11.7% | Copy buttons |
If your target site lazy-loads images with IntersectionObserver (40%), uses a service worker (39%), or streams a response body (32%), those percentages are the ones to read before the 86.66% headline. --load-resources being opt-in for iframes, workers and external stylesheets multiplies this: by default, a page’s worker code does not run at all.
One page per browser process
This is the constraint that will decide whether Lightpanda drops into an existing pipeline, so I tested it the way a Playwright user would.
playwright.chromium.connect_over_cdp("http://127.0.0.1:9222") succeeds — the browser reports version 124.0.6367.29 and user agent Lightpanda/1.0. Then:
Browser.new_context: Protocol error (Target.createBrowserContext):
Cannot have more than one browser context at a time
BrowserContext.new_page (second page): Protocol error (Target.createTarget):
TargetAlreadyLoaded
Page.goto on the page you were given at connect:
Protocol error (Page.navigate):
No page on the STARTUP session; create a real target with Target.createTarget
So the working shape is exactly one sequence: connect, new_context(), new_page(), goto(). A second page in the same context fails. A second context fails. The page that exists before you create a context cannot navigate.
This is consistent with the benchmark document’s flat statement that Lightpanda “can’t create multi-tabs”, and it explains the 100-processes row. It also means the usual crawler design — one browser, N pages, a worker pool over tabs — does not port. You get parallelism by running N processes, each on its own port, exactly as the benchmark’s --fork mode does. The supported in-process alternative is the MCP HTTP transport, where each Mcp-Session-Id gets its own browsing session; that is a different API surface from CDP.
For a single-page agent that navigates one site at a time, none of this matters. For a scraper with a tab pool, it is a rewrite — and it is the kind of rewrite you want to discover on day one, not in week three.
Three things the text dump does differently from a browser
Lightpanda’s product for an agent is text: --dump markdown, --dump semantic_tree_text, --dump structured_data. Three measured differences from Chromium’s innerText:
1. A page’s own “your scripts did not run” notice survives. On python.org, Lightpanda’s markdown contains the hidden #nojs block verbatim — “This page displays a fallback because interactive scripts did not run” — and getComputedStyle on that element returns display: block. Chromium hides it. In Lightpanda’s own log the page’s three script URLs execute, three XHRs return 200, and the same log records not_implemented feature=webgl. Why the page still believes its scripts failed is not documented by either side; what is documented is that Lightpanda reports unsupported features at info level, where nobody looks.
2. visibility:hidden text is dumped. I served a page with one visible marker and eight hiding techniques and compared both browsers:
| Hiding technique | Lightpanda markdown | Chromium innerText |
|---|---|---|
style="display:none" | excluded | excluded |
display:none from a stylesheet | excluded | excluded |
aria-hidden="true" | excluded | excluded |
hidden attribute | excluded | excluded |
<template> content | excluded | excluded |
<noscript> content | excluded | excluded |
visibility:hidden | included | excluded |
position:absolute; left:-9999px | included | included |
Seven of eight match. The one that does not is visibility:hidden — the technique sites use for visually-hidden labels, skip links and, occasionally, things they would rather a scraper did not read.
3. robots.txt is opt-in, and block pages arrive as base64. The flag is --obey-robots; without it nothing is checked. I fetched a Reddit thread both ways. With the flag: Navigation failed … Reason: RobotsBlocked, which is the correct behaviour. Without it: Reddit’s block page, whose inline data:image/png;base64 payload was dumped into the markdown verbatim — tens of kilobytes of base64 in a single document. --dump-max-bytes exists for exactly this, and if you are feeding the output to a model, you want it.
Two smaller edges found the same way: file:// URLs fail with UnsupportedProtocol, so local HTML fixtures have to be served over HTTP; and --dump markdown first asks the server for markdown via content negotiation (“markdown served” in the log), which means on sites that serve markdown to agents the output may not be the page’s DOM at all. Useful, and worth knowing when a diff looks strange.
A recipe that respects the constraint
Everything below ran on the 2-vCPU box, against the 2 November nightly.
# 1. Install (glibc 2.35+ on x86_64; 2.38+ on arm64 — Alpine will not work)
curl -L -o lightpanda \
https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux
chmod a+x ./lightpanda
./lightpanda version # 1.1.0-nightly.11471+cebb8d51e
# 2. One page, text out — the cheapest useful call
./lightpanda fetch --dump markdown --log-level error https://example.com/
# 3. CDP server for Playwright/Puppeteer
./lightpanda serve --host 127.0.0.1 --port 9222 --log-level info
// 4. Playwright: one context, one page, and reuse that page for every URL
const { chromium } = require('playwright');
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222');
const context = await browser.newContext(); // only one of these, ever
const page = await context.newPage(); // and only one of these
for (const url of urls) {
await page.goto(url, { waitUntil: 'domcontentloaded' });
const text = await page.evaluate(() => document.body.innerText);
}
# 5. Parallelism: N processes on N ports, not N tabs
for p in 9222 9223 9224 9225; do
./lightpanda serve --host 127.0.0.1 --port $p --log-level warn &
done
Two flags worth adopting before the first production run: --obey-robots, and LIGHTPANDA_DISABLE_TELEMETRY=true — telemetry is on by default, and the README says so in one line.
FAQ
Is Lightpanda a Chromium fork?
No. It is written in Zig, uses V8 for JavaScript, html5ever for HTML parsing and libcurl for HTTP, with no Blink and no rendering engine. That is the source of both its memory advantage and its CSS gap: 31.9% of the css WPT suite passes, and CSS failures are 42.6% of all its failing subtests.
How many WPT subtests does it really pass?
1,841,066 of 2,124,349 on the 1 November 2026 run — 86.66%, against 2,184,491 for Chrome per the project’s own comparison. Of those passes, 1,151,555 (62.5%) come from the encoding suite and 191,425 (10.4%) from wasm; removing both leaves 498,086 subtests of browser-feature coverage, and only 30.4% of test files pass completely.
Can I open several pages in one browser instance?
Not through CDP as of the 2 November nightly. A second new_page() in the same context raises TargetAlreadyLoaded; a second new_context() raises Cannot have more than one browser context at a time. Parallelism means one process per port, which is what the project’s own benchmark does.
Does it take screenshots?
No rendering pipeline. --dump png and --dump pdf render the page’s markdown into an image, which is useful for logs and useless for verifying layout. The 1.0 post is explicit that its own agent product falls back to Chrome for screenshots.
Will it get blocked by anti-bot systems? Fingerprinting relies on exactly what Lightpanda omits — canvas and WebGL output, font enumeration, layout metrics. The maintainers said in January 2025 that they had not worked on bot detection, and nothing in the current docs claims otherwise. For bot-protected sites, assume Chrome.
Can I use it in a commercial product? It is AGPL-3.0, with a CLA for contributions, and the founders have said the licence was chosen so that a company running the browser as a managed service must publish its modifications. Driving it across a process or CDP boundary is a different question from embedding it in your binary, and it is a question for your lawyer, not this article.
The verdict
Lightpanda is doing something no other project is doing: a browser that runs the modern web’s JavaScript at 70 MB, in a 77 MB binary, one process per page, with a 118 ms fetch on a trivial page. For an agent that must read HTML-ish pages on a small VM, that is a real and measurable win on this hardware — 2.3x on wall time and 8.5x on memory, at concurrency 1, with every page succeeding.
The claims around it are looser than the engineering. “9x faster” is 1.6x at equal concurrency; “16x lighter” is 47x at equal concurrency and 16x at twenty-five processes; “1.7 million WPT subtests” is 27% of that number in browser features once two generated suites are set aside. The benchmark page contradicts itself on instance type and page count, and its Chrome 100-tab cell has a typo that turns 69 seconds into an hour. None of that is dishonest — each figure is traceable to a real row — but the composition is never in the headline, and the composition is what decides whether your page renders.
The honest summary is narrower than the marketing and more useful: Lightpanda is a fast, memory-cheap, one-page-per-process browser that runs JavaScript well, layout not at all, and multi-tab never. If your target pages are content and forms, keep it. If they are layouts, lazy-loading widgets, workers or captchas, measure the specific suites above before you migrate — and run your own ten pages first, because the difference between 86.66% and your page is one page.