Lightpanda — Headless Browser for AI & Automation
Skill by ara.so — Daily 2026 Skills collection
Lightpanda is a headless browser built from scratch in Zig, designed for AI agents, web scraping, and automation. It uses 9x less memory and runs 11x faster than Chrome headless.
Key facts:
-
Not based on Chromium, Blink, or WebKit — clean-slate Zig implementation
-
JavaScript execution via V8 engine
-
CDP (Chrome DevTools Protocol) compatible — works with Playwright, Puppeteer, chromedp
-
Respects robots.txt via --obey_robots flag
-
Beta status, actively developed
-
License: AGPL-3.0
Installation
macOS (Apple Silicon)
curl -L -o lightpanda https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-aarch64-macos chmod a+x ./lightpanda
Linux (x86_64)
curl -L -o lightpanda https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux chmod a+x ./lightpanda
Docker
Supports amd64 and arm64
docker run -d --name lightpanda -p 9222:9222 lightpanda/browser:nightly
CLI Usage
Fetch a URL (dump rendered HTML)
./lightpanda fetch --obey_robots --log_format pretty --log_level info https://example.com
Start CDP Server
./lightpanda serve --obey_robots --log_format pretty --log_level info --host 127.0.0.1 --port 9222
This launches a WebSocket-based CDP server for programmatic control.
CLI Flags
Flag Description
--obey_robots
Respect robots.txt rules
--log_format pretty
Human-readable log output
--log_level info
Log verbosity: debug , info , warn , error
--host 127.0.0.1
Bind address for CDP server
--port 9222
Port for CDP server
--insecure_disable_tls_host_verification
Disable TLS verification (testing only)
Playwright Integration
Start the CDP server, then connect Playwright to it:
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222'); const context = await browser.contexts()[0] || await browser.newContext(); const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' }); const title = await page.title(); const content = await page.content();
console.log(Title: ${title});
console.log(HTML length: ${content.length});
await browser.close();
Puppeteer Integration
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.connect({ browserWSEndpoint: 'ws://127.0.0.1:9222', });
const context = await browser.createBrowserContext(); const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
const title = await page.title(); const text = await page.evaluate(() => document.body.innerText);
console.log(Title: ${title});
console.log(Body text: ${text.substring(0, 200)});
await page.close(); await browser.close();
Go (chromedp) Integration
package main
import ( "context" "fmt" "log"
"github.com/chromedp/chromedp"
)
func main() { allocCtx, cancel := chromedp.NewRemoteAllocator(context.Background(), "ws://127.0.0.1:9222") defer cancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
var title string
err := chromedp.Run(ctx,
chromedp.Navigate("https://example.com"),
chromedp.Title(&title),
)
if err != nil {
log.Fatal(err)
}
fmt.Println("Title:", title)
}
Python Integration
import asyncio from playwright.async_api import async_playwright
async def main(): async with async_playwright() as p: browser = await p.chromium.connect_over_cdp("http://127.0.0.1:9222") context = browser.contexts[0] if browser.contexts else await browser.new_context() page = await context.new_page()
await page.goto("https://example.com", wait_until="networkidle")
title = await page.title()
content = await page.content()
print(f"Title: {title}")
print(f"HTML length: {len(content)}")
await browser.close()
asyncio.run(main())
Web Scraping Patterns
Batch Page Fetching
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222'); const context = await browser.newContext();
const urls = [ 'https://example.com/page1', 'https://example.com/page2', 'https://example.com/page3', ];
for (const url of urls) { const page = await context.newPage(); await page.goto(url, { waitUntil: 'networkidle' });
const data = await page.evaluate(() => ({ title: document.title, text: document.body.innerText, links: [...document.querySelectorAll('a[href]')].map(a => a.href), }));
console.log(JSON.stringify(data, null, 2)); await page.close(); }
await browser.close();
Extract Structured Data
const data = await page.evaluate(() => { const items = document.querySelectorAll('.product-card'); return [...items].map(item => ({ name: item.querySelector('h2')?.textContent?.trim(), price: item.querySelector('.price')?.textContent?.trim(), link: item.querySelector('a')?.href, })); });
Docker Compose (with your app)
services: lightpanda: image: lightpanda/browser:nightly ports: - "9222:9222" restart: unless-stopped
scraper: build: . depends_on: - lightpanda environment: - BROWSER_WS_ENDPOINT=ws://lightpanda:9222
Supported Web APIs
Lightpanda supports (partial, expanding):
-
DOM tree manipulation and querying
-
JavaScript execution (V8)
-
XMLHttpRequest (XHR)
-
Fetch API
-
Cookie management
-
Network interception
-
Proxy support
Configuration
Environment Variable Description
LIGHTPANDA_DISABLE_TELEMETRY
Set to true to opt out of usage metrics
Performance Comparison
Metric Lightpanda Chrome Headless
Memory ~9x less Baseline
Speed ~11x faster Baseline
Binary size Small (Zig) Large (Chromium)
Rendering No visual rendering Full rendering engine
When to Use Lightpanda
Use Lightpanda when:
-
Running AI agents that need to browse the web
-
Batch scraping at scale (memory/CPU savings matter)
-
Automating form submissions and data extraction
-
Running in containers where resources are limited
-
You need CDP compatibility but not full visual rendering
Use Chrome/Playwright instead when:
-
You need pixel-perfect screenshots or PDF generation
-
You need full Web API coverage (Lightpanda is still partial)
-
Visual regression testing
-
Testing browser-specific rendering behavior
Building from Source
Requires: Zig 0.15.2, Rust, CMake, system dependencies.
Ubuntu/Debian dependencies
sudo apt install xz-utils ca-certificates pkg-config libglib2.0-dev clang make curl
Build
git clone https://github.com/lightpanda-io/browser.git cd browser zig build
Optional: pre-build V8 snapshot for faster startup
zig build snapshot_creator -- src/snapshot.bin zig build -Dsnapshot_path=../../snapshot.bin
Troubleshooting
Connection refused on port 9222:
-
Ensure ./lightpanda serve is running
-
Check --host 0.0.0.0 if connecting from Docker/remote
Playwright script breaks after update:
-
Lightpanda is beta — Playwright's capability detection may behave differently across versions
-
Pin your Lightpanda version or use nightly consistently
Missing Web API support:
-
Check the zig-js-runtime repo for current API coverage
-
File issues at lightpanda-io/browser
Links
-
Runtime APIs: https://github.com/lightpanda-io/zig-js-runtime