Lightpanda y Obscura abren las mismas páginas que Chrome headless, pero un agente que haga algo más que leer notará la diferencia. Los probé el 28 de septiembre de 2026 en Docker sobre arm64, con Lightpanda 0.4.1, Obscura 0.2.3 y el shell headless de Chrome 154, conducidos por Playwright y Puppeteer a través del protocolo Chrome DevTools (CDP). Chrome superó las 17 pruebas funcionales de Playwright, Lightpanda pasó 10 y Obscura otras 10, pero fallaron en sitios distintos. Si buscas qué es cada motor, empieza por qué es Obscura, el navegador headless en Rust; aquí están los fallos, con los errores tal como salieron. También tienes la versión en inglés de esta comparativa.

Puntos clave

  • Los tres cargaron las siete webs reales de la prueba, una aplicación React 19.3, cookies y 50 páginas locales sin un solo fallo, y los tres generan capturas PNG y PDF.
  • Lightpanda 0.4.1 no carga iframes, workers ni hojas de estilo externas salvo que lo pidas con --load-resources, y solo admite un contexto y una página por conexión CDP.
  • Obscura 0.2.3 rompe page.selectOption de Playwright, hace clics en shadow DOM que no llegan al botón y tiene un IndexedDB que no guarda nada.
  • En una tarea de agente sobre Wikipedia, Chrome completó los 7 pasos; Lightpanda y Obscura se quedaron en 6, los dos al hacer clic en un enlace.
  • Con 8 sesiones y 50 páginas locales, Lightpanda cargó 276 páginas por segundo, Obscura 196 y Chrome 49,6; el pico de memoria del contenedor fue de 160, 132 y 1038 MiB.
  • La licencia también decide: Lightpanda es AGPL-3.0 y Obscura, Apache-2.0.

Qué son Lightpanda, Obscura y Chrome headless

Los tres son navegadores sin interfaz que un programa controla por CDP, pero solo Chrome es un navegador completo. Lightpanda y Obscura son motores escritos desde cero que ejecutan JavaScript con V8 y construyen su propio DOM, sin el compositor ni la pila de renderizado de Chromium. Esa renuncia es su argumento: menos memoria y arranque más corto por sesión.

Chrome headless shell Lightpanda Obscura
Versión probada 154.0.8037.57 0.4.1 0.2.3
Fecha de la versión Estable en CfT el 28 de septiembre de 2026 15 de septiembre de 2026 21 de septiembre de 2026
Lenguaje del motor C++ (Chromium) Zig Rust
Licencia BSD, código de Chromium AGPL-3.0 Apache-2.0
Imagen arm64 comprimida Propia, sobre Debian 80,3 MB 99,6 MB
Protocolos CDP CDP y WebDriver BiDi CDP

El shell headless de Chrome es el antiguo modo headless que Google publica como binario aparte. Su blog para desarrolladores[1] lo describe así: "The old Headless shell is a lightweight wrapper around Chromium’s //content module, and it therefore has substantially fewer dependencies". Playwright lo usa por defecto en modo headless, según su guía de navegadores[2]. Google publica builds linux-arm64 en Chrome for Testing[3] desde la versión 153.0.8001.0, así que ya no hace falta compilar Chromium para usarlo en arm64.

Lightpanda ha publicado seis versiones entre el 1 de julio y el 15 de septiembre de 2026. Las notas de Lightpanda 0.4.0[4], del 1 de septiembre, cambiaron los valores por defecto: "Iframes and workers no longer load by default". La 0.4.1 añadió CORS experimental y trabajo de conformidad en IndexedDB. Obscura 0.2.3, según sus notas de versión[5], exige un token de al menos 32 bytes cuando el puerto CDP escucha fuera de loopback.

Cómo monté la prueba en arm64

Cada navegador corre en su propio contenedor y los scripts de Playwright y Puppeteer se conectan desde fuera por CDP. La máquina es una VM Linux arm64 de 18 núcleos (OrbStack sobre Apple silicon). Las cifras de rendimiento son medianas de 5 repeticiones tomadas el 28 de septiembre, con la carga media de un minuto entre 1,73 y 2,78 al empezar cada una.

Las páginas de prueba las sirve un nginx en la misma red de Docker. Estos son los servicios de Lightpanda y Obscura en el compose.yaml:

  lightpanda:
    image: lightpanda/browser:0.4.1
    command: ["/bin/lightpanda", "serve", "--host", "0.0.0.0",
      "--port", "24402", "--advertise-host", "127.0.0.1"]
    ports: ["127.0.0.1:24402:24402"]
  obscura:
    image: h4ckf0r0day/obscura:0.2.3
    command: ["serve", "--port", "24403", "--host", "0.0.0.0",
      "--allow-private-network"]
    environment:
      OBSCURA_CDP_TOKEN: ${OBSCURA_CDP_TOKEN}
    ports: ["127.0.0.1:24403:24403"]

Montarlo costó tres tropiezos, uno por navegador:

  • Obscura: sin token, /json/version responde {"error":"authentication required"}. Con token pero publicando el puerto 9222 del contenedor en el 24403 del anfitrión, responde {"error":"request refused"}, porque rechaza una cabecera Host con un puerto distinto del suyo. Por eso el puerto publicado y el de escucha coinciden. --allow-private-network hace falta para abrir páginas en IP privadas.
  • Lightpanda: --advertise-host hace que /json/version anuncie una URL WebSocket alcanzable desde fuera del contenedor.
  • Chrome 154: escuchó en 127.0.0.1 dentro del contenedor aunque le pasé --remote-debugging-address=0.0.0.0. Lo resolví con un socat en la misma imagen que reenvía el puerto 9223 al 9222.

Para Playwright, la conexión es connectOverCDP con la cabecera del token. Así abre Obscura la aplicación React de prueba:

import { chromium } from 'playwright-core';

const browser = await chromium.connectOverCDP('http://127.0.0.1:24403', {
  headers: { Authorization: `Bearer ${process.env.OBSCURA_CDP_TOKEN}` },
});
const ctx = await browser.newContext();
const page = await ctx.newPage();
await page.goto('http://b6p4-site/03-react.html');
await page.locator('.ritem').first().waitFor();
console.log(await page.locator('.ritem').allInnerTexts());
await browser.close();

Usé playwright-core 1.63.0 y puppeteer-core 25.12.0. La documentación de Playwright[6] avisa de que esta vía es un compromiso: "This connection is significantly lower fidelity than the Playwright protocol connection via browserType.connect()". Para Lightpanda y Obscura no hay otra, porque Playwright solo lanza sus propios navegadores. Chrome, conectado igual, sirve de referencia para separar los fallos del motor de los de la conexión.

Qué funcionó igual en los tres navegadores

Las tareas de lectura funcionaron en los tres, que es para lo que más se usa un navegador en un agente. Las pruebas comunes fueron estas:

  • Una página estática, una SPA que pide datos con fetch y cambia de ruta con history.pushState, y una aplicación React 19.3 con un filtro que se rellena con fill
  • Cookies, incluida la que llega en una cabecera Set-Cookie, localStorage y sessionStorage
  • Una excepción lanzada dentro de page.evaluate que llega al script con su mensaje
  • Siete webs reales: example.com, Hacker News, un artículo de Wikipedia, un repositorio de GitHub, react.dev, MDN y jacar.es, todas con HTTP 200 y sin errores de JavaScript
  • Un conjunto fijo de 50 páginas locales (artículos, SPA en React y páginas con JavaScript pesado), cargadas en serie y con 4 y 8 sesiones en paralelo, sin fallos

Las capturas de pantalla y los PDF también salen en los tres, pero no son la misma imagen. Según las notas de la 0.4.0, la captura de Lightpanda "renders the page’s markdown": dibuja una versión en texto de la página, no la maquetación. Obscura sí maqueta y pinta, y se acerca más a Chrome:

Captura compuesta de la misma página HTML estática en Chrome 154, Lightpanda 0.4.1 y Obscura 0.2.3. Lightpanda convierte la tabla en filas separadas por barras porque su captura dibuja la versión Markdown.

Dónde falla Lightpanda 0.4.1

Lightpanda carga lo mínimo por defecto y trata cada conexión CDP como una sola sesión con una sola página. Con los valores de fábrica, un iframe no carga, un worker no llega a ejecutarse y una hoja de estilo externa no se aplica. Por eso Playwright dio por visibles dos elementos que el CSS ocultaba. La ayuda de lightpanda serve lo deja claro en --load-resources: "Defaults to requesting none of them".

Con --load-resources iframe, worker y stylesheet, el iframe y el worker funcionaron, y display: none y visibility: hidden se respetaron. El resto del CSS no llegó al estilo calculado: el color del texto siguió en rgb(0, 0, 0) y la opacidad de un pseudoelemento ::after, en 1.

El límite que más pesa en un agente es el de contextos. Un segundo browser.newContext() sobre la misma conexión devolvió este error:

Protocol error (Target.createBrowserContext): Cannot have more
than one browser context at a time

Una segunda página dentro del mismo contexto falló con Protocol error (Target.createTarget): TargetAlreadyLoaded. La concurrencia en Lightpanda pasa por abrir una conexión CDP por sesión, y así sí funcionó con cuatro sesiones en paralelo.

Su servidor acepta 16 conexiones simultáneas por defecto (--cdp-max-connections); Obscura, 128. El propio banco de pruebas de Lightpanda compara procesos de Lightpanda con pestañas de Chrome. Este patrón, una conexión por sesión, es el que corrió:

async function session(url) {
  const browser = await chromium.connectOverCDP('http://127.0.0.1:24402');
  const ctx = await browser.newContext();
  const page = await ctx.newPage();
  await page.goto(url);
  const title = await page.title();
  await browser.close();
  return title;
}
console.log(await Promise.all(urls.map(session)));

Lightpanda no maqueta como un navegador, y eso rompe los clics por coordenadas. Playwright no pudo pulsar un botón dentro de un shadow DOM ("<html>…</html> intercepts pointer events"). Tampoco un enlace de Wikipedia que Lightpanda situaba a 7920 píxeles de altura en una caja de 5 por 5 ("element is outside of the viewport").

La geometría tampoco es real: document.elementFromPoint devuelve null, el canvas no pinta y toDataURL devuelve un PNG de 1×1, algo que las notas de la 0.4.0 reconocen. Un scroll infinito basado en IntersectionObserver añadió una tanda de filas al hacer scroll, donde Chrome y Obscura añadieron tres.

Dónde falla Obscura 0.2.3

Obscura maqueta de verdad y admite más de un contexto y más de una página por conexión, pero sus fallos son más traicioneros: algunos no dan error. El más visible rompe page.selectOption, la forma habitual de elegir una opción de un desplegable con Playwright:

page.selectOption: TypeError: Cannot read properties of undefined
(reading 'replace')
    at normalizeWhiteSpace (eval at <anonymous> ...)
    at InjectedScript.selectOptions (eval at <anonymous> ...)

La causa está en el DOM de Obscura: option.label devuelve null donde Chrome devuelve "ES", y el código que Playwright inyecta en la página no lo espera. page.select de Puppeteer sí funcionó. La instantánea de accesibilidad (ARIA) muestra el desplegable como combobox "Language": es, sin sus opciones, y un agente que lea esa instantánea no sabrá qué valores puede elegir.

Los otros fallos no avisan:

  • Shadow DOM: Playwright y Puppeteer dieron el clic por bueno, pero el contador del botón siguió a 0. El evento no llegó al elemento dentro de la raíz shadow; un click() desde JavaScript sí.
  • IndexedDB: abrir la base de datos funciona, pero onupgradeneeded nunca se dispara y un get después de un put devuelve undefined.
  • Interceptar peticiones: con page.route(...) y fulfill, locator.waitFor() agotó el tiempo aunque el elemento ya estaba en el DOM e isVisible() devolvía true. Pasó en las 11 ejecuciones en que lo repetí; waitForFunction sí funcionó.
  • Iframes: el iframe ejecuta su script y habla con la página por postMessage, pero frameLocator('#f') de Playwright no encuentra nada dentro. Lightpanda, con iframes activados, falla igual.

Hay dos diferencias más que afectan a lo que un agente lee. innerText incluye texto oculto con CSS: en la página de prueba devolvió el párrafo con display: none, y en el repositorio de GitHub dio 111 970 caracteres frente a los 14 449 de Chrome. Además, Obscura se presenta con dos identidades: /json/version dice Chrome 145 sobre Linux x86_64, y navigator.userAgent dentro de la página, Chrome 143 sobre Windows, sin haber activado --stealth.

Tabla de compatibilidad con Playwright

La batería de Playwright tiene 17 pruebas con un resultado comprobable, y corrió completa cuatro veces por navegador con los mismos fallos. La primera fila agrupa tres pruebas, y la columna de Lightpanda completo usa --load-resources iframe, worker y stylesheet:

Prueba Chrome 154 Lightpanda Lightpanda completo Obscura
Página estática, SPA y React Sí Sí Sí Sí
Iframe con frameLocator Sí No carga Carga; frameLocator falla Carga; frameLocator falla
Web Worker Sí No se ejecuta Sí Sí
CSS externo y visibilidad Sí No se aplica Solo visibilidad Falla ::after
Formulario con selectOption Sí Sí Sí TypeError
Cookies y localStorage Sí Sí Sí Sí
IndexedDB Sí Sí Sí No guarda
Clic en shadow DOM Sí Clic bloqueado Clic bloqueado Clic sin efecto
Canvas Sí No pinta No pinta Sí
Scroll infinito Sí Una tanda de tres Una tanda de tres Sí
Captura PNG y PDF Sí Versión Markdown Versión Markdown Sí, con diferencias
Instantánea ARIA Sí Sí Sí Sin opciones
route con fulfill Sí Sí Sí waitFor se cuelga
Dos contextos a la vez Sí Error Error Sí
Error en evaluate Sí Sí Sí Sí
Total 17 de 17 10 de 17 11 de 17 10 de 17

Con Puppeteer, la batería de 10 pruebas dio 10 en Chrome y 9 en los otros dos; el fallo fue el mismo, el clic en shadow DOM. La prueba de API web añadió un matiz: la página se sirve por HTTP, y Chrome oculta crypto.subtle y crypto.randomUUID fuera de un contexto seguro. Lightpanda y Obscura los exponen igual, y Obscura también navigator.serviceWorker y navigator.clipboard. navigator.webdriver vale true en Chrome y false en los otros dos.

Qué pasa con una tarea de agente real

Una tarea corta sobre Wikipedia separa bien leer de actuar. El script abre la portada, toma la instantánea ARIA, escribe "Headless browser" en el buscador por su rol, pulsa Intro, lee la primera frase, sigue el enlace "Chromium" y lee el título. Localiza los elementos por rol y nombre, igual que un agente que trabaja sobre instantáneas de accesibilidad:

Paso Chrome 154 Lightpanda Obscura
Abrir, instantánea, buscar, leer Sí Sí Sí
Clic en el enlace "Chromium" Sí Fuera del viewport Sin navegación
Pasos completados 7 de 7 6 de 7 6 de 7

El fallo de Obscura es el peligroso. Playwright dio el clic por hecho, pero la página no cambió. document.elementFromPoint en el centro del enlace devolvió otro enlace, la nota al pie "[27]", porque Obscura sitúa los elementos en posiciones distintas a las de Chrome.

Un agente que no compruebe la URL después del clic seguiría trabajando sobre la página equivocada. En Lightpanda, un click() desde JavaScript sobre el enlace sí navegó. En Obscura, ese mismo click() navegó, pero evaluate lanzó "Execution context was destroyed"; lo fiable es leer el href y llamar a page.goto.

Cuánto tardan y cuánto consumen

Con el conjunto local de 50 páginas, Lightpanda fue el más rápido y Obscura el que menos memoria de contenedor usó; Chrome quedó lejos en las dos cosas. Cada cifra es la mediana de 5 repeticiones, con el navegador reiniciado al empezar cada una:

Arranque en frío hasta la primera página ms · menos es mejor
Chrome 154
340 ms
Lightpanda
147 ms
Obscura
146 ms
Ver tabla de valores
Navegadorms
Chrome 154340 ms
Lightpanda147 ms
Obscura146 ms

Mediana de 5 repeticiones con 50 páginas locales, con el navegador reiniciado al empezar cada una.

Tiempo para cargar 50 páginas en serie segundos · menos es mejor
Chrome 154
3,32 s
Lightpanda
0,88 s
Obscura
1,19 s
Ver tabla de valores
Navegadorsegundos
Chrome 1543,32 s
Lightpanda0,88 s
Obscura1,19 s

Mediana de 5 repeticiones con 50 páginas locales, con el navegador reiniciado al empezar cada una.

Páginas por segundo con sesiones en paralelo páginas/s · más es mejor
Chrome 154
4 sesiones: 28,9
8 sesiones: 49,6
Lightpanda
4 sesiones: 185
8 sesiones: 276
Obscura
4 sesiones: 137
8 sesiones: 196
Ver tabla de valores
Navegador4 sesiones8 sesiones
Chrome 15428,949,6
Lightpanda185276
Obscura137196

Mediana de 5 repeticiones con 50 páginas locales, con el navegador reiniciado al empezar cada una.

Memoria de cada navegador MiB · menos es mejor
Chrome 154
Contenedor, pico con 8 sesiones: 1038 MiB
RSS sumada, pico con 8 sesiones: 2281 MiB
RSS sumada en reposo: 489 MiB
Lightpanda
Contenedor, pico con 8 sesiones: 160 MiB
RSS sumada, pico con 8 sesiones: 179 MiB
RSS sumada en reposo: 22 MiB
Obscura
Contenedor, pico con 8 sesiones: 132 MiB
RSS sumada, pico con 8 sesiones: 169 MiB
RSS sumada en reposo: 38 MiB
Ver tabla de valores
NavegadorContenedor, pico con 8 sesionesRSS sumada, pico con 8 sesionesRSS sumada en reposo
Chrome 1541038 MiB2281 MiB489 MiB
Lightpanda160 MiB179 MiB22 MiB
Obscura132 MiB169 MiB38 MiB

El pico del contenedor es memory.current de su cgroup; la RSS sumada exagera la de Chrome.

La RSS sumada junta la memoria residente de todos los procesos del contenedor. Exagera la de Chrome, que reparte el trabajo entre procesos que comparten páginas, así que la fila del contenedor (el pico de memory.current de su cgroup) es la comparación justa. Las 50 páginas cargaron sin fallos en las 15 series de cada navegador. Ojo con Lightpanda: por defecto no descarga las hojas de estilo, y parte de su ventaja sale de trabajar menos.

En la web real, la red manda. Estas son las medianas de 5 cargas por página hasta el evento load, en un contexto nuevo cada vez y alternando los navegadores:

Carga de páginas reales hasta el evento load ms · menos es mejor
example.com
Chrome 154: 51 ms
Lightpanda: 44 ms
Obscura: 97 ms
Hacker News
Chrome 154: 1220 ms
Lightpanda: 1040 ms
Obscura: 1520 ms
Artículo de Wikipedia
Chrome 154: 447 ms
Lightpanda: 776 ms
Obscura: 1400 ms
Repositorio de GitHub
Chrome 154: 477 ms
Lightpanda: 493 ms
Obscura: 3600 ms
react.dev
Chrome 154: 410 ms
Lightpanda: 397 ms
Obscura: 1090 ms
Ver tabla de valores
Página realChrome 154LightpandaObscura
example.com51 ms44 ms97 ms
Hacker News1220 ms1040 ms1520 ms
Artículo de Wikipedia447 ms776 ms1400 ms
Repositorio de GitHub477 ms493 ms3600 ms
react.dev410 ms397 ms1090 ms

Mediana de 5 cargas por página, en un contexto nuevo cada vez y alternando los navegadores.

Lightpanda empató o mejoró a Chrome en cuatro de las cinco páginas y perdió en Wikipedia. Obscura fue el más lento en las cinco, y en GitHub tardó 7,5 veces más que Chrome.

Qué dicen mis cifras de lo que prometen los proyectos

Los dos proyectos venden rendimiento, y sus cifras son suyas, no mías. El README de Lightpanda anuncia "16x lighter and 9x faster than Chromium" sobre 933 páginas reales: 123 MB de memoria pico frente a 2 GB, con 100 páginas. El README habla de una instancia m5.large y el documento de benchmarks de Lightpanda[7], de una m5.xlarge con Chrome 143, ambas x86_64.

El banco de pruebas de Obscura[8] mide 98 páginas públicas. Obscura renderizó 94 y Chrome headless 85, con una memoria pico mediana de 64,2 MB frente a 201,2 MB. El mismo informe reconoce que la latencia mediana fue peor en Obscura, 5,2 s frente a 2,1 s.

Mis cifras confirman la dirección, no los multiplicadores. Con 8 sesiones, Lightpanda cargó 5,6 veces más páginas por segundo que Chrome y usó 6,5 veces menos memoria de contenedor, lejos del 9 y el 16 de su README. Obscura gastó 7,9 veces menos memoria que Chrome, más que el factor 3 de su propio informe, y su latencia peor en páginas reales también apareció aquí.

Qué navegador elegir para tu agente

Elige según lo que hace tu agente con la página, no según la cifra de memoria del README. Con lo que vi en estas pruebas:

  • Chrome headless: para cualquier agente que rellene formularios, haga clic por coordenadas, trabaje con iframes o dependa de capturas fieles. Es el único que pasó todo.
  • Lightpanda: para leer y extraer a escala, con una conexión CDP por sesión. Activa --load-resources stylesheet si tu agente decide por visibilidad, y cuenta con la AGPL-3.0 si lo expones como servicio.
  • Obscura: para extracción con más de una pestaña por conexión y capturas aproximadas, cuando la licencia Apache-2.0 importa. Evita selectOption en Playwright y comprueba la URL después de cada clic.
  • Un servicio de extracción: si solo quieres el contenido limpio, Firecrawl te da los datos web para tu agente sin operar el navegador.

Si tu agente decide solo dónde hacer clic, como los que describe Browser Use, agentes que navegan, prueba el motor ligero en paralelo y compara las URL finales antes de cambiar. Cambiar de motor cuesta una URL de conexión, así que la prueba es barata.

Preguntas frecuentes

¿Puedo usar Lightpanda u Obscura con Playwright sin cambiar mi código?

Sí para leer, no siempre para actuar. Cambias launch() por connectOverCDP() con la URL del servidor, y en Obscura añades la cabecera del token. El código de lectura funcionó sin tocarlo; selectOption en Obscura y los contextos múltiples en Lightpanda obligan a reescribir partes.

¿Funcionan Lightpanda y Obscura en arm64?

Sí. Las dos publican imágenes de Docker multiarquitectura con arm64 y binarios para Linux aarch64, y las dos arrancaron sin cambios en esta prueba. Chrome for Testing publica el shell headless para linux-arm64 desde la versión 153.

¿Cuál gasta menos memoria, Lightpanda u Obscura?

Obscura, por poco. Con 8 sesiones sobre las 50 páginas locales, el contenedor de Obscura llegó a 132 MiB, el de Lightpanda a 160 MiB y el de Chrome a 1038 MiB. En reposo la ventaja se invierte: tras la primera página, Lightpanda ocupaba 22 MiB de RSS y Obscura, 38 MiB.

Conclusión

Lightpanda y Obscura ya abren la web real por CDP, y para leer páginas sustituyen a Chrome con un cambio de URL. En velocidad y memoria los dos dejan atrás a Chrome en páginas locales, pero no en la web real. Donde se separan es al actuar.

Lightpanda falla de forma visible y predecible: un contexto por conexión, recursos desactivados y clics por coordenadas que no llegan. Obscura cubre más superficie de Playwright, pero algunos de sus fallos, como el clic en shadow DOM o el enlace equivocado de Wikipedia, no dan error.

Si tu agente escribe, pulsa y elige, sigue con Chrome headless o valida cada acción. Si solo lee, cualquiera de los dos sirve, y la licencia puede decidir por ti.

Fuentes: [1] Lightpanda 0.4.1, notas de la versión[9], [2] Lightpanda 0.4.0, notas de la versión[4], [3] Repositorio de Lightpanda[10], [4] Benchmarks de Lightpanda[7], [5] Obscura 0.2.3, notas de la versión[5], [6] Repositorio de Obscura[11], [7] obscura-benchmark[8], [8] Chrome for Testing[3], [9] Chrome headless shell, blog de Chrome[1], [10] Playwright, connectOverCDP[6].

Fuentes

  1. blog para desarrolladores
  2. guía de navegadores
  3. Chrome for Testing
  4. notas de Lightpanda 0.4.0
  5. notas de versión
  6. documentación de Playwright
  7. documento de benchmarks de Lightpanda
  8. banco de pruebas de Obscura
  9. Lightpanda 0.4.1, notas de la versión
  10. Repositorio de Lightpanda
  11. Repositorio de Obscura