How do I switch my Playwright project to use Lightpanda via connectOverCDP (example code + endpoint format)?
Headless Browser Infrastructure

How do I switch my Playwright project to use Lightpanda via connectOverCDP (example code + endpoint format)?

8 min read

Most Playwright setups assume there’s a full Chrome stack running somewhere—great for humans, wasteful for machines. In the cloud, that bloat shows up as slow cold starts, high memory peak, and painful concurrency limits. When you’re automating “machines talking to websites,” those are not implementation details; they’re core product constraints.

Lightpanda was built to flip that. It’s a headless-first browser, written from scratch in Zig, exposing a Chrome DevTools Protocol (CDP) endpoint so your existing Playwright scripts can connect via connectOverCDP without a rewrite. The only thing you really swap is the endpoint URL.

Below is a runbook for switching your Playwright project to Lightpanda—first with a local Lightpanda process, then with Lightpanda Cloud, using concrete endpoint formats and copy-pastable code.


Quick Answer:
To switch Playwright to Lightpanda, replace your chromium.launch() call with chromium.connectOverCDP(<LIGHTPANDA_ENDPOINT>).

  • Local: chromium.connectOverCDP('ws://127.0.0.1:9222') after starting ./lightpanda serve.
  • Cloud: chromium.connectOverCDP('wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN').
    The rest of your test or crawler logic stays the same.

Why switch Playwright to Lightpanda?

If you’re already at the point where you’re reading an article with a slug like how-do-i-switch-my-playwright-project-to-use-lightpanda-via-connectovercdp-examp, you probably already feel the pain:

  • Cold starts compound with concurrency. Spinning up many Chrome instances in the cloud adds seconds of latency each time, multiplied by every parallel worker.
  • Memory peak limits your parallelism. On an m5.large, you can’t realistically run a high number of concurrent Chrome-based workers without fighting OOM or noisy-neighbor issues.
  • You pay for UI baggage you never render. Headless Chrome still carries rendering and UI infrastructure that’s irrelevant when no human ever sees the page.

We built Lightpanda specifically around those pain points:

  • In our own Puppeteer 100-page benchmark on an AWS EC2 m5.large, Lightpanda finished in ~2.3s vs Headless Chrome’s ~25.2s, with ~9× lower memory peak (~24MB vs ~207MB).
  • Startup is effectively instant, which matters when you’re spinning up and tearing down browser processes continuously for crawling, testing, or agent workflows.

Playwright doesn’t need to know any of that. It only needs a CDP endpoint. That’s why the migration is mostly “change the connection string.”


Quick comparison: Chrome vs Lightpanda with Playwright

AspectHeadless Chrome (typical)Lightpanda (local or Cloud)
Startup timeMulti-second cold start per processInstant startup
Memory peak (100 pages)~207MB (m5.large, Puppeteer benchmark)~24MB (~9× less)
Connection modeLocal launch() or custom remote CDPLocal connectOverCDP('ws://127.0.0.1:9222') or Cloud wss://…
Rendering/UI overheadCarries full legacy rendering stackHeadless-first, no UI/rendering baggage
Best use caseHuman-centric browsing, legacy compatibilityMachine-first automation at cloud scale

Core pattern: use chromium.connectOverCDP

Playwright offers two main ways to run Chromium:

  • chromium.launch() – start a browser process directly (local Chrome/Chromium).
  • chromium.connectOverCDP() – connect to an existing browser via CDP.

Lightpanda exposes a CDP server. So the switch is:

// Before: local Chromium
const browser = await chromium.launch({ headless: true });

// After: Lightpanda via CDP
const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');
// or, for Cloud:
const browser = await chromium.connectOverCDP('wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN');

Everything after browser is obtained—newContext(), newPage(), locators, assertions—stays the same.


Option 1: Connect Playwright to Lightpanda running locally

This path is ideal when you’re prototyping, running tests on your laptop, or validating that Lightpanda behaves correctly for your target sites before you move into Cloud.

1. Install dependencies

You’ll need:

  • playwright-core (or @playwright/test if you’re using the test runner)
  • Lightpanda’s browser package (for local CLI/serve, if you’re consuming it via npm) or the Lightpanda binary from GitHub.

Example with npm:

npm install playwright-core
# or: npm install @playwright/test

Download Lightpanda from GitHub if you haven’t already, and make it available as ./lightpanda (or in your PATH).

2. Start the Lightpanda CDP server

Lightpanda exposes CDP over WebSocket. A common default is:

  • Host: 127.0.0.1
  • Port: 9222

You can either:

A. Run from CLI:

./lightpanda serve --host 127.0.0.1 --port 9222

or

B. Start from a Node process (using the npm package pattern):

import { lightpanda } from '@lightpanda/browser';

const lpdopts = { host: '127.0.0.1', port: 9222 };

const proc = await lightpanda.serve(lpdopts);
// Lightpanda is now listening on ws://127.0.0.1:9222

Either way, you now have a CDP endpoint at:

ws://127.0.0.1:9222

3. Connect Playwright with connectOverCDP

Here’s a minimal script that uses Playwright with Lightpanda, mirroring the pattern from our docs.

// index.ts or index.js
import { chromium } from 'playwright-core';

// CDP endpoint exposed by Lightpanda
const LIGHTPANDA_ENDPOINT = 'ws://127.0.0.1:9222';

(async () => {
  // 1) Connect to Lightpanda via CDP
  const browser = await chromium.connectOverCDP(LIGHTPANDA_ENDPOINT);

  // 2) Create a new browser context and page
  const context = await browser.newContext({});
  const page = await context.newPage();

  // 3) Use Playwright as usual
  await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle' });
  console.log('Page title:', await page.title());

  // 4) Clean up
  await page.close();
  await context.close();
  await browser.disconnect();
})();

This is effectively the same as the official snippet:

import { chromium } from 'playwright-core';

// use connectOverCDP to pass the Lightpanda's CDP server address.
const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');

// The rest of your script remains the same.

4. Swap in your existing tests or crawler

In a typical Playwright test or script, you’re currently doing something like:

const browser = await chromium.launch({ headless: true });
// ...
const context = await browser.newContext();
const page = await context.newPage();

Change just the first line:

const browser = await chromium.connectOverCDP('ws://127.0.0.1:9222');

That’s the core of “switching your Playwright project to Lightpanda via connectOverCDP.”


Option 2: Connect Playwright to Lightpanda Cloud (recommended for scale)

Local Lightpanda gives you the behavior and API. Lightpanda Cloud gives you the scale: regioned endpoints, managed orchestration, and the ability to run many concurrent sessions with quick cold starts.

The connection pattern is almost identical; you just use a wss:// endpoint with a token.

1. Get your Cloud token and region endpoint

When you create a Lightpanda Cloud account, you’ll see:

  • A token (API-style secret).
  • One or more region endpoints, e.g.:
    • wss://euwest.cloud.lightpanda.io/ws
    • wss://uswest.cloud.lightpanda.io/ws

The base endpoint format is:

wss://<region>.cloud.lightpanda.io/ws?token=YOUR_TOKEN

For example:

wss://euwest.cloud.lightpanda.io/ws?token=lp_abc123

2. Connect Playwright to the Cloud endpoint

Here’s a minimal Playwright script wired to Lightpanda Cloud:

import { chromium } from 'playwright-core';

const LIGHTPANDA_CLOUD_ENDPOINT =
  'wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN';

(async () => {
  // 1) Connect to Lightpanda Cloud via CDP
  const browser = await chromium.connectOverCDP(LIGHTPANDA_CLOUD_ENDPOINT);

  // 2) Standard Playwright flow
  const context = await browser.newContext({});
  const page = await context.newPage();

  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  console.log('Loaded URL:', page.url());

  // 3) Cleanup
  await page.close();
  await context.close();
  await browser.disconnect();
})();

If you prefer the generic playwright-core import style, the docs example is:

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();
// ...

3. Optional: Proxies via query parameters

In Cloud, you can configure proxies via query parameters on the endpoint URL (for example, datacenter location or country). The concrete formats evolve, but the pattern is:

wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN&proxy_dc=...&proxy_country=...

You don’t change your Playwright code; you only adjust the endpoint string.


Bringing it together: full before/after comparison

Before: Playwright with local Chrome/Chromium

import { chromium } from '@playwright/test';

test('basic navigation', async () => {
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);

  await page.close();
  await context.close();
  await browser.close();
});

After: Playwright with Lightpanda via connectOverCDP (local)

import { chromium } from '@playwright/test';

const LIGHTPANDA_ENDPOINT = 'ws://127.0.0.1:9222';

test('basic navigation with Lightpanda', async () => {
  const browser = await chromium.connectOverCDP(LIGHTPANDA_ENDPOINT);
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);

  await page.close();
  await context.close();
  await browser.disconnect();
});

After: Playwright with Lightpanda Cloud via connectOverCDP

import { chromium } from '@playwright/test';

const LIGHTPANDA_CLOUD_ENDPOINT =
  'wss://euwest.cloud.lightpanda.io/ws?token=YOUR_TOKEN';

test('basic navigation with Lightpanda Cloud', async () => {
  const browser = await chromium.connectOverCDP(LIGHTPANDA_CLOUD_ENDPOINT);
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);

  await page.close();
  await context.close();
  await browser.disconnect();
});

The test body is identical. The difference is the browser connection and the underlying runtime footprint.


Responsible crawling and testing at scale

Because Lightpanda makes it easy to run many fast, cheap browser sessions, it’s important to stay disciplined:

  • Respect robots.txt. In local mode you can enable this via the CLI flag:

    ./lightpanda serve --obey_robots
    
  • Avoid high-frequency hammering. When you go from “Chrome starts in a few seconds” to “Lightpanda starts instantly,” the risk of accidental DDoS goes up. Keep explicit rate limits in your Playwright code, especially in crawlers.

  • Isolate sessions. One of the reasons we built Lightpanda is to avoid shared cookies/sessions across automation jobs. Use fresh contexts per job and don’t rely on shared browser state.


Migration checklist

If you want a concrete checklist to switch your Playwright project to Lightpanda via connectOverCDP:

  1. Decide your mode:

    • Local Lightpanda (ws://127.0.0.1:9222) for dev / validation.
    • Cloud Lightpanda (wss://<region>.cloud.lightpanda.io/ws?token=...) for scale.
  2. Start Lightpanda:

    • Local CLI: ./lightpanda serve --host 127.0.0.1 --port 9222
    • Or ensure Cloud endpoint and token are configured.
  3. Change your connection code:

    • Replace chromium.launch() with chromium.connectOverCDP(<LIGHTPANDA_ENDPOINT>).
  4. Keep the rest of Playwright as-is:

    • browser.newContext(), context.newPage(), locators, assertions, etc., do not change.
  5. Add cleanup and observability:

    • Always await browser.disconnect().
    • Use Lightpanda Cloud’s console to inspect recent sessions and debug failures.

Once you’ve done this, you’re effectively running Playwright on a browser that was actually designed for machines in the cloud, not humans in front of a screen.


Next step

If you want to see the impact on your own workload—whether it’s regression tests, crawlers, or agent workflows—the fastest path is to wire your existing Playwright project to Lightpanda using the connectOverCDP pattern above, then compare execution time and memory use under load.

Get Started