
How do I switch my Playwright project to use Lightpanda via connectOverCDP (example code + endpoint format)?
Running Playwright at scale on Headless Chrome is where the cracks show: multi‑second cold starts, 200+MB peaks per process, and fragile remote orchestration. Lightpanda exists to fix exactly that—while letting you keep your existing Playwright code by connecting over CDP.
This guide shows how to switch an existing Playwright project to Lightpanda using connectOverCDP, with copy‑paste examples for:
- Local Lightpanda (open source binary)
- Lightpanda Cloud (managed CDP endpoints)
- Endpoint URL formats, including tokens and proxies
Why switch Playwright to Lightpanda via connectOverCDP?
If you’re already using playwright-core with Chrome/Chromium, the failure mode at cloud scale is familiar:
- Each browser process is heavy (hundreds of MB).
- Cold starts are measured in seconds.
- Running hundreds or thousands of concurrent sessions gets expensive and brittle.
Lightpanda gives you:
- Instant startup (no multi‑second Chrome boot)
- ~9× less memory per process in our Puppeteer 100‑page benchmark (24MB vs 207MB on an AWS EC2 m5.large)
- ~11× faster execution in the same test (2.3s vs 25.2s)
- Headless‑first design: no rendering/UI baggage, purpose‑built for machines
And you can adopt it without rewriting your automation: Playwright connects to Lightpanda using Chromium’s CDP driver (connectOverCDP). The rest of your script can stay the same.
At-a-glance: switching Playwright to Lightpanda
Here’s the minimal shape of the change:
import { chromium } from 'playwright-core';
// Before: launching bundled browser
// const browser = await chromium.launch();
// After: connect to Lightpanda via CDP
const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');
const context = await browser.newContext();
const page = await context.newPage();
// ...your existing logic...
You’re just replacing launch() with connectOverCDP() and pointing at a Lightpanda CDP endpoint.
Option 1 – Use Lightpanda locally with Playwright (connectOverCDP)
This is the fastest way to try Lightpanda in an existing Playwright project: run the browser locally, then connect over CDP.
1. Install dependencies
Make sure you’re using playwright-core (not the bundled browser flavor):
npm install playwright-core @lightpanda/browser
2. Start Lightpanda via the Node helper
Lightpanda ships a helper that can spawn the browser for you. Typical local defaults:
- Host:
127.0.0.1 - Port:
9222(CDP server)
Example script (index.ts or index.mjs):
import { lightpanda } from '@lightpanda/browser';
import { chromium } from 'playwright-core';
const lpdopts = {
host: '127.0.0.1',
port: 9222,
};
const playwrightopts = {
endpointURL: `ws://${lpdopts.host}:${lpdopts.port}`,
};
(async () => {
// 1) Start Lightpanda browser in a separate process.
const proc = await lightpanda.serve(lpdopts);
// 2) Connect Playwright's chromium driver to Lightpanda over CDP.
const browser = await chromium.connectOverCDP(playwrightopts);
const context = await browser.newContext({});
const page = await context.newPage();
// 3) Do your magic ✨ (your existing Playwright logic)
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle' });
console.log('CDP connection is working');
// 4) Clean up: close Playwright + stop Lightpanda.
await page.close();
await context.close();
await browser.disconnect();
// Depending on your setup, you can also terminate the process explicitly:
await proc.kill(); // or the appropriate method in your version
})();
You can simplify it further if you run Lightpanda separately from your tests.
3. Run Lightpanda as a standalone process (alternative)
You can also start Lightpanda via CLI and keep your Node script focused on Playwright:
./lightpanda serve --host 127.0.0.1 --port 9222
Then just connect in your Playwright script:
import { chromium } from 'playwright-core';
// Use connectOverCDP to pass Lightpanda's CDP server address.
const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');
// The rest of your script remains the same.
const context = await browser.newContext();
const page = await context.newPage();
// ...
This approach mirrors how you’d point Playwright at a remote Chromium instance, but with Lightpanda’s instant startup and smaller memory peak.
Option 2 – Use Lightpanda Cloud with Playwright (wss endpoint + token)
When you don’t want to run the browser yourself, Lightpanda Cloud exposes regioned CDP WebSocket endpoints you can plug into from any environment.
The connection pattern with playwright-core looks like this:
import playwright from 'playwright-core';
const browser = await playwright.chromium.connectOverCDP(
'wss://euwest.cloud.lightpanda.io/ws?token=TOKEN',
);
const context = await browser.newContext();
const page = await context.newPage();
// Your existing Playwright logic...
await page.goto('https://example.com');
Cloud endpoint format
A production Cloud endpoint has the following structure:
wss://<region>.cloud.lightpanda.io/ws?token=<YOUR_TOKEN>[&proxy=...]
- Protocol:
wss://(secure WebSocket) - Host: e.g.
euwest.cloud.lightpanda.io,uswest.cloud.lightpanda.io - Path:
/ws - Query:
token: your Lightpanda Cloud token- optional proxy parameters (e.g. datacenter + country) depending on your plan
Example:
const endpoint = 'wss://uswest.cloud.lightpanda.io/ws?token=lp_abc123';
const browser = await chromium.connectOverCDP(endpoint);
Adding proxies via the endpoint
Cloud lets you configure proxies directly in the URL via query parameters. A typical pattern is:
wss://euwest.cloud.lightpanda.io/ws?token=lp_abc123&proxy=datacenter-us
Then in code:
const browser = await chromium.connectOverCDP(
'wss://euwest.cloud.lightpanda.io/ws?token=lp_abc123&proxy=datacenter-us',
);
Check your Lightpanda Cloud console for the exact proxy parameters available to your account.
Session visibility and cleanup
Cloud keeps a short history of recent sessions in the console so you can debug what your agents were doing.
In code, the teardown pattern is:
await page.close();
await context.close();
await browser.disconnect();
Disconnecting cleanly is important when you run many short‑lived sessions (crawlers, agents, tests) so resources are released quickly.
Mapping your existing Playwright code to Lightpanda
If you’ve already got a test or crawler suite in Playwright, the migration is mostly mechanical.
From launch() to connectOverCDP()
Before (bundled Chromium):
import { chromium } from 'playwright';
const browser = await chromium.launch({
headless: true,
});
const context = await browser.newContext();
const page = await context.newPage();
After (Lightpanda via CDP):
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');
// or: 'wss://euwest.cloud.lightpanda.io/ws?token=TOKEN'
const context = await browser.newContext();
const page = await context.newPage();
Most of your script—selectors, assertions, DOM logic, network handling—stays unchanged.
Using the endpointURL options object
If you prefer a structured options object instead of a raw string:
import { chromium } from 'playwright-core';
const lpdopts = {
host: '127.0.0.1',
port: 9222,
};
const playwrightopts = {
endpointURL: `ws://${lpdopts.host}:${lpdopts.port}`,
};
const browser = await chromium.connectOverCDP(playwrightopts);
const context = await browser.newContext({});
const page = await context.newPage();
This maps cleanly onto configuration files or environment‑driven setups in CI.
Running crawlers and agents responsibly with Lightpanda
Lightpanda is built for scale—instant startup and low memory per process mean it’s easy to accidentally run very high concurrency.
The same rules for responsible automation still apply:
-
Respect
robots.txt
When running the Lightpanda binary, you can enable robots compliance through the--obey_robotsflag:./lightpanda fetch --obey_robots https://example.comFor CDP‑driven flows, use the same flag on the serve/fetch commands your infrastructure runs.
-
Watch your request rate
Because Lightpanda spins up so fast, an aggressive job or agent loop can send a lot of traffic in a short time. Keep per‑domain concurrency and delay controls in your orchestration layer; DDOS can happen accidentally when each browser instance is cheap. -
Isolate sessions
Avoid shared browser state between users or jobs. Lightpanda’s model is to favor isolated environments rather than persistent, shared cookies/sessions—which is the right default for untrusted or multi‑tenant automation.
Example: migrating a real Playwright script to Lightpanda (end-to-end)
Here’s a practical end‑to‑end migration to make the change concrete.
Original script (local Chromium):
import { chromium } from 'playwright';
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://news.ycombinator.com');
const title = await page.title();
console.log('Title:', title);
await browser.close();
})();
After migration to Lightpanda Cloud:
import { chromium } from 'playwright-core';
(async () => {
// 1) Connect to Lightpanda Cloud via CDP.
const browser = await chromium.connectOverCDP(
'wss://euwest.cloud.lightpanda.io/ws?token=lp_abc123',
);
// 2) The rest remains almost identical.
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle' });
const title = await page.title();
console.log('Title:', title);
// 3) Clean teardown.
await page.close();
await context.close();
await browser.disconnect();
})();
After migration to local Lightpanda (open source):
import { lightpanda } from '@lightpanda/browser';
import { chromium } from 'playwright-core';
const lpdopts = { host: '127.0.0.1', port: 9222 };
(async () => {
const proc = await lightpanda.serve(lpdopts);
const browser = await chromium.connectOverCDP(
`ws://${lpdopts.host}:${lpdopts.port}`,
);
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle' });
const title = await page.title();
console.log('Title:', title);
await page.close();
await context.close();
await browser.disconnect();
await proc.kill();
})();
In both cases, the logical behavior of the test/crawler is unchanged; only the browser implementation behind CDP has been swapped with a headless‑first engine.
Telemetry, privacy, and enterprise controls
Lightpanda’s core browser is open source and will remain so. When you run it yourself, you get:
-
A clear privacy policy around telemetry.
-
An explicit opt‑out via the environment variable:
export LIGHTPANDA_DISABLE_TELEMETRY=true
For teams with advanced security or uptime requirements, the Cloud and enterprise paths add:
- regioned CDP endpoints (e.g.
euwest,uswest) - SLA and uptime guarantees
- options for on premise or private cloud
- better isolation primitives (multi‑context, sandboxing) tuned for agent and crawler workloads
These are all reachable without changing your adoption model: still CDP, still connectOverCDP(), just different endpoint URLs and tokens.
What to do next
If you’re hitting Chrome’s limits in your Playwright projects—cold starts, memory bloat, flaky remote control—the easiest next step is to plug Lightpanda in over CDP and measure:
- Swap
launch()forconnectOverCDP()against a local Lightpanda instance. - Run your existing test or crawler suite.
- Compare runtime and memory footprint; treat cold start and peak memory as first‑class metrics.
You keep Playwright. You keep CDP. You just replace the legacy, UI‑first browser with a headless‑first engine that was actually built for the cloud.