What Is a True Headless Browser?

Pierre Tachoire
CTO

TL;DR
Most “headless” browsers aren’t really headless. They’re just regular consumer browsers running with the display turned off. A true headless browser skips all the graphical rendering, building only what programs need (the DOM tree) while ignoring everything designed for human eyes (images, fonts, CSS layout). This approach can reduce resource usage by 60-80% for automation workloads.
What “Headless” Usually Means
When you launch Chrome with the --headless flag, Chrome starts up completely, loads its entire rendering engine, processes CSS, downloads fonts and images, calculates layouts, paints pixels to framebuffers.
# launching "headless" Chrome
$ google-chrome --headlessThis made sense when headless mode was created. The Chrome team took their existing codebase and added a flag to skip displaying the window. Minimal code changes, maximum compatibility. Every website that works in regular Chrome works in headless Chrome because it’s literally the same browser.
But for automation it’s massive overkill.
What Programs Actually Need
Let’s break down what happens when a browser loads a web page:
- Fetch HTML over the network
- Parse HTML into a DOM (Document Object Model) tree
- Fetch and execute JavaScript that manipulates the DOM
- Fetch and parse CSS to apply styling rules
- Calculate layout (where everything goes on screen)
- Fetch images and fonts for display
- Paint pixels to render the visual result
- Composite layers for smooth scrolling and animations
Automation scripts only interact with steps 1-3. You navigate to a page, you query the DOM for elements, you interact with those elements through JavaScript.
// What your automation actually does
await page.goto('https://example.com'); // Step 1-3
const price = await page.$eval('.price', el => // Step 3 (DOM query)
el.textContent
);
await page.click('#buy-button'); // Step 3 (DOM interaction)Steps 4-8 exist entirely for human eyeballs. A program doesn’t care about the exact button’s position, whether the font is Arial or Helvetica, or if there’s a smooth fade-in animation. The program just needs to know the button exists in the DOM and can be clicked.
The Real Cost of Rendering
CSS Processing
CSS parsers convert stylesheets into rule trees. Layout engines calculate positions, sizes, margins, and paddings for every element. For a typical e-commerce page with 2,000+ DOM nodes and hundreds of CSS rules, this becomes serious computation.
Your automation script doesn’t care if the price is positioned 20 pixels from the top or 200 pixels. It just needs <span class="price">$29.99</span> from the DOM.
Image Decoding
Modern web pages load dozens of images: product photos, logos, icons, background images. Chrome downloads them, decodes JPEG/PNG/WebP formats into pixel data, and stores them in texture memory.
For automation extracting text data, every image download is wasted bandwidth and every decoded image is wasted bandwidth, time and memory.
Font Loading
Web fonts have become standard. A single page might load 3-4 font files totaling several megabytes. Chrome downloads them, parses the font metrics, and prepares glyph rendering.
Your scraper reading <h1>Product Name</h1> only needs the text content, not the custom typeface.
Layout Calculation
This is where Chrome figures out where every box goes on screen. It’s complex: flexbox, grid, floats, absolute positioning, responsive breakpoints. The engine recalculates layouts when the DOM changes or the window resizes.
Automation doesn’t need real x/y coordinates. It queries elements by selectors and reads their attributes.
What Does “True Headless” Mean?
A true headless browser builds only what programs need: the DOM tree and JavaScript execution environment. Here’s what Lightpanda does differently:
| Chrome | Lightpanda | |
|---|---|---|
| Fetch HTML over the network | Yes | Yes |
| Parse HTML into a DOM tree | Yes | Yes |
| Fetch and execute JavaScript that manipulates the DOM | Yes | Yes |
| Fetch and parse CSS to apply styling rules | Yes | No |
| Calculate layout | Yes | No |
| Fetch images and fonts for display | Yes | No |
| Paint pixels to render the visual result | Yes | No |
| Composite layers for smooth scrolling and animations | Yes | No |
The result is a DOM tree in memory that your automation can interact with through standard APIs. When you call document.querySelector('.price'), you get the element. When you call element.textContent, you get the text. The DOM exists but the visual rendering doesn’t.
// Same automation code, different engine
const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://127.0.0.1:9222 // Lightpanda endpoint
});
// This works identically
const page = await browser.newPage();
await page.goto('https://example.com');
const price = await page.$eval('.price', el => el.textContent);The DOM tree structure is identical. The JavaScript execution works the same but under the hood, Lightpanda never downloads a single font or image and never calculates a single layout dimension.
What You Give Up
Lightpanda makes tradeoffs. You need to understand what you’re giving up:
No Visual Regression Testing
You can’t take screenshots of pages that aren’t rendered. If you need to verify that button’s exact position or that an image displays correctly, you need actual rendering.or
Lightpanda uses the same API as Chrome (CDP). This means you can run the same script in your workflows if you need to take a screenshot at buildtime, but not every time you extract the data or take an action on the page.
No Layout-Dependent Interactions
Some interactions depend on visual layout. “Click the element at coordinates (100, 200)” requires knowing where things are positioned. “Click the third visible item” requires knowing which items are actually visible based on CSS display properties.
Lightpanda handles selector-based interactions fine: “click the button with class ‘submit’” works because that’s a DOM query. Coordinate-based interactions don’t work because there are no real coordinates.
When to Use Lightpanda
Here’s our recommended decision framework:
Use Lightpanda when:
- Extracting data (scraping, monitoring, indexing)
- Running functional tests that check behavior, not appearance
- Automating form submissions and workflows
- Generating reports from web-based dashboards
- Running at scale where resource costs matter
Use Headless Chrome when:
- Taking screenshots or PDFs
- Visual regression testing
- Testing responsive design breakpoints
- Debugging layout issues
- Working with sites that absolutely require rendering
Use Headful Chrome when:
- Developing and debugging automation scripts
- Need to see what’s happening in real-time
- Testing actual user experience
Ready to Experience What a True Headless Browser Feels Like?
Making the Switch
If you’re currently using Puppeteer or Playwright with Headless Chrome, switching to Lightpanda requires minimal code changes. The Chrome DevTools Protocol (CDP) compatibility means your automation scripts mostly work as-is.
Start by testing your automation suite against Lightpanda in a development environment. For scripts that don’t work, you can run hybrid setups and fallback to Chrome when you need rendering.
Ready to try it?
- Get started and run your first Lightpanda script in under 10 minutes
- Read the docs to learn how to connect with Puppeteer or Playwright
- Star the project on GitHub to stay up with the latest developments