Introducing Lightpanda Agent and PandaScript: LLM at buildtime not runtime - Blog | Lightpanda
Back<br>Introducing Lightpanda Agent and PandaScript: LLM at buildtime not runtime<br>Francis Bouvier<br>Cofounder & CEO<br>IconXIconLinkedin
announcement automation agents<br>Wednesday, June 17, 2026<br>TL;DR
A browser agent today is four separate tools wired together: Chrome, a CDP<br>library, an LLM, and an agent framework. Our native agent collapses those four<br>layers into one binary.
You talk to it in natural language and it does the work against a real browser.<br>“Go to this website”, “login”, “extract this data”, “tell me that”: language is<br>the new native interface for the browser. And if you want to replay your<br>session, it hands you a reproducible script. No CDP, no heavy browser server,<br>no complex setup, no LLM at runtime: everything is built in into Lightpanda.
Your browser agent is four tools. It should be one.
To run a browser agent today, you install Chrome, drive it over CDP with<br>Puppeteer or Playwright, wire in an LLM to make decisions, then wrap the whole<br>thing in an agent framework to orchestrate the calls. It works. Well, kind of:<br>it’s a lot of moving parts for what’s often, “load a page and read some<br>elements off it.”
Each of those layers exists to translate between a human-facing browser and a<br>machine that wants to drive it. CDP (the Chrome DevTools Protocol) was built so<br>a developer could inspect a running browser from the outside, not so a program<br>could operate one. And to wire an LLM, you need an MCP-to-CDP layer. The agent<br>wraps a model around a tool that was never meant to be driven by a model. You<br>are paying a translation tax at every layer.
When we started Lightpanda four years ago, the question was: “what would a<br>browser look like if you built it for machines instead of people looking at a<br>screen?” lightpanda agent is that bet applied to the agentic stack. It is one<br>binary that contains the browser, the runtime, and the agent.
The browser is the same engine behind lightpanda serve and lightpanda fetch: it loads webpages, runs JavaScript, and handles the DOM.
The runtime consists of a small set of native tools (goto, click, fill,<br>extract, evaluate, search), that let you drive the browser (slash commands). The<br>LLM that reads your request and picks tools is optional. It runs against<br>Anthropic, OpenAI, Gemini, Hugging Face or local Ollama, or with no key at all<br>in slash-only mode.
Download the demo
The LLM runs at buildtime, not runtime
This is the idea that everything else hangs off. Every other browser agent is a<br>black box that calls a model on every step. With lightpanda agent, the model<br>figures out the task, we capture that work as code, and generate a script (we<br>call it PandaScript) that is reproducible and deterministic. When you replay<br>it, you don’t need a model and the LLM is gone from the loop.
There’s no model call sitting between you and the next action at replay time,<br>no Chrome process to host, no NodeJS/Python environment to setup, and no<br>Playwright/Puppeteer code to write. You just pass the script to Lightpanda<br>binary, so a run is bound only by how fast the engine drives the page. And<br>because nothing non-deterministic runs at replay, the same script produces the<br>same result every time. You pay the model once to write the file. After that,<br>it’s plain JavaScript that you own.
Here’s what that PandaScript looks like. This script grabs the top 3 Hacker<br>News stories, then visits each thread for its top comments:
const HN_ORIGIN = "https://news.ycombinator.com";<br>const page = new Page();<br>await page.goto(HN_ORIGIN);<br>const stories = page.extract([{<br>selector: "tr.athing", limit: 3,<br>fields: {<br>id: { attr: "id" }, rank: ".rank", title: ".titleline > a",<br>url: { selector: ".titleline > a", attr: "href" }<br>}]);<br>for (const story of stories) {<br>await page.goto(HN_ORIGIN + "/item?id=" + story.id);<br>try {<br>story.comments = page.extract([{<br>selector: "tr.athing.comtr:has(.commtext)", limit: 3,<br>fields: {<br>author: ".hnuser",<br>text: ".commtext"<br>}]);<br>} catch { story.comments = []; }<br>return stories;
Readable vanilla JavaScript with loops, map, filter, and try/catch. And a few<br>built-in primitives for our native tools. That’s it. The last top-level<br>expression auto-prints as JSON.
And of course you can also write or edit a PandaScript manually, or ask your AI<br>coding assistant to do so if you prefer.
PandaScript doesn’t use CDP, by design
Lightpanda browser still speaks CDP with lightpanda serve, and we are actively<br>developing it. The decision here is narrower: we chose not to put CDP inside<br>the agent.
Traditional headless automation marshals every action across CDP, with hundreds<br>of methods running against a browser in a separate process.. Our agent skips<br>that. It runs in-process against Lightpanda’s engine and calls a small set of<br>native commands directly.
This gives you two things:
There is no serialization overhead , because a native in-process call replaces marshalling every click and fill across a wire...