Lightpanda
View on GitHubHeadless browser written from scratch in Zig. No compositor, no layout, no GPU. 16x less memory and 9x faster than headless Chrome on a 100-page benchmark.
About
Lightpanda is a headless browser written from scratch in Zig, not a Chromium fork or a WebKit patch. The first commit landed in February 2023; the project crossed nearly 30,000 GitHub stars and ships under AGPL-3.0. The pitch on the README is two numbers: 100 web pages crawled in 5 seconds against headless Chrome's 46 seconds, with a peak memory footprint of 123MB versus Chrome's 2GB. Those numbers come from a benchmark on an AWS m5.large; reproducing them is a matter of cloning lightpanda-io/demo.
The defining design choice is what Lightpanda chose not to build. There is no rendering engine, no compositor, no layout pass, no GPU. The browser only does what an AI agent or a crawler needs: fetch the document, parse the HTML, build the DOM, run the JavaScript, and let you dump the result. The README puts it bluntly under Why Lightpanda?: "Many features are not necessary in headless mode." Cutting them is how the memory and speed numbers get to where they are.
Architecturally, Lightpanda is one Zig binary that links three foreign engines into one address space. v8 from the lightpanda-io/zig-v8-fork dependency runs the JavaScript. libcurl 8.18 handles every HTTP request. html5ever, the Servo HTML5 parser, builds the DOM through C ABI callbacks back into Zig. The Zig code owns the DOM tree, the page lifetime, the v8 isolate, and the memory layout. Around 75% of the source is Zig; the rest is Rust glue for html5ever and C bindings to v8 and curl.
Architecture
The repository root is small. build.zig is the build graph; build.zig.zon declares the dependencies (v8 fork, brotli, zlib, nghttp2, BoringSSL, curl 8.18, sqlite3, libidn2). The interesting code is all under src/. The top-level src/lightpanda.zig file is the public module surface and reads like a directory listing of the architecture: App, Network, Server, Browser, Session, Page, Frame, js, HttpClient, plus dump, markdown, SemanticTree, and cookies.
HTTP is libcurl, with three Zig layers on top of it. src/network/http.zig wraps the C handles and slist headers. The next layer up, src/network/Network.zig, holds the curl multi handle and a Zig allocator (ZigToCurlAllocator) that fronts curl's malloc/calloc/realloc/free hooks so every byte curl allocates flows through Zig's allocator interface. The per-browser client lives in src/browser/HttpClient.zig and adds a layered request pipeline (cache, robots, web bot auth, interception).
HTML parsing is html5ever, the same parser Servo uses, compiled as a Rust static library and linked in. The Rust side exposes html5ever_parse_document (and a streaming variant) in src/html5ever/lib.rs as extern "C" functions that take a callback table for tree-building operations. On the Zig side, src/browser/parser/html5ever.zig declares the same functions as extern "c", and src/browser/parser/Parser.zig implements the callbacks: createElementCallback, appendCallback, popCallback, and a dozen others. The DOM tree is owned by Zig; html5ever just drives its construction.
JavaScript is v8 via the lightpanda-io/zig-v8-fork dependency. src/browser/js/Env.zig creates one v8 isolate per browser, loads the snapshot blob from src/browser/js/Snapshot.zig, registers the function templates for every Web API class, and installs callbacks for promise rejection, dynamic module imports, and OOM handling. Each Page gets its own v8 Context; same-origin frames share an Origin for object identity. The Web API surface lives under src/browser/webapi/, with about sixty files covering Document, Element, Event, MutationObserver, IntersectionObserver, Crypto, Navigator, and the rest.
The headless event loop lives in src/browser/Runner.zig, with no compositor anywhere in the picture. _wait is a polling loop that calls _tick, which drives libcurl's multi handle, runs queued navigations, executes pending scripts, and pumps v8's microtask and macrotask queues. Once per second the runner sends v8 a moderate memory pressure notification so wrappers and external references get freed during long-running pages. fetch mode runs the loop until --wait-until, --wait-selector, or --wait-script resolves, then dumps HTML or markdown to stdout.
Start here
The Architecture tour walks the build graph, the module map, the libcurl HTTP layer, the html5ever Rust-to-Zig parser bridge, the Zig DOM construction callbacks, the v8 isolate setup, and the headless run loop. Read it first if you want to see how a non-Chromium browser is actually wired together.
Start the Post-Chrome Browser Architecture tour
The Memory tour covers the Zig side of the same system: the GPA-versus-c_allocator split in main, the per-page arena pool, the FFI ownership boundaries with libcurl and html5ever, errdefer-based cleanup, and the v8 memory pressure notifications that produce the 123MB vs 2GB number from the README.
Tours
Getting Started with Your First Contribution to Lightpanda
beginnerWalk through the README quick start, the Makefile build targets, the CLI run command, the test harness, the src/browser/webapi/ extension point, and CONTRIBUTING.md.
Post-Chrome Browser Architecture
intermediateWalk the Zig build graph, the module map, the libcurl HTTP layer, the html5ever Rust-to-Zig parser bridge, the v8 embedding, and the headless event loop.
Zig Memory Management for a Browser
advancedFrom the GPA-vs-c_allocator split in main, through the per-page arena pool, to the libcurl allocator boundary and the v8 memory pressure hints.
Maintainers
Francis Bouvier is co-founder of Lightpanda (Selecy SAS) and one of two names on every copyright header in the repository. He works on the project full-time out of France and posts as the primary public voice on Twitter and the Lightpanda Discord.
Pierre Tachoire (GitHub handle krichprollsch) is the second co-founder of Lightpanda and the second name on every copyright header. He has the second-highest commit count on the repository and works on the project full-time alongside Francis.
Karl Seguin is the top contributor on the repository by commit count, with around 2,500 commits at the pinned SHA. He is the author of the openmymind.net engineering blog and has shipped large parts of the v8 bridge, the HTTP client, the arena pool, and the browser runtime.
Origin Story
Lightpanda: A Headless Browser Without a RendererWhy Francis Bouvier and Pierre Tachoire wrote a new browser in Zig instead of forking Chromium, and what the 123MB vs 2GB number really measures.
Read the full storyRelated Projects
Create code tours for your project
Intraview lets AI create interactive walkthroughs of any codebase. Install the free VS Code extension and generate your first tour in minutes.
Install Intraview Free