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

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_robots flag:

    ./lightpanda fetch --obey_robots https://example.com
    

    For 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:

  1. Swap launch() for connectOverCDP() against a local Lightpanda instance.
  2. Run your existing test or crawler suite.
  3. 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.

Get Started