Lightpanda es un navegador escrito desde cero en Zig, sin Chromium y sin motor de renderizado, apuntado a la carga de trabajo que más creció en 2026: agentes que leen la web. Salió de beta el 2 de octubre de 2026 con la versión 1.0, y el 2 de noviembre de 2026 el repositorio marca 40.121 estrellas. Su README abre con una promesa de dos líneas —“16x lighter and 9x faster than Chromium”— y una tabla.
Descargué la compilación nightly, la ejecuté contra diez páginas reales junto al Chromium de Playwright, me conecté a su servidor CDP con Playwright y después analicé el JSON crudo que hay detrás de su panel público de Web Platform Tests en lugar de leer el contador de aprobados. De ese trabajo salieron tres cosas que el README no dice.
Primero, los 1,84 millones de aprobados en WPT son sobre todo una suite: encoding aporta 1.151.555 y wasm otros 191.425; juntas, el 72,9% del total. Segundo, “9x faster” es un resultado de escalado por concurrencia, no por página: a igual concurrencia la diferencia es de 1,6 veces, y el 9x aparece solo al comparar 25 procesos de sistema operativo de Lightpanda contra 25 pestañas de Chrome. Tercero, y lo más práctico: Lightpanda sirve una página por proceso de navegador. El patrón habitual de Playwright de abrir una segunda página en el mismo contexto falla con un error de protocolo, y un segundo contexto de navegador directamente no se puede crear.
Los números primero
| Dato | Valor | Fuente |
|---|---|---|
| Estrellas / forks / seguidores | 40.121 / 1.962 / 98 | API de GitHub, 2026-11-02 00:46 UTC |
| Seguidores como porcentaje de estrellas | 0,24% | calculado |
| Contribuidores / commits | 84 / 11.486 | API de GitHub, contributors |
| Tres primeros contribuidores | 4.991, 2.227 y 1.677 commits (77,4% del total) | API de GitHub |
| Contribuidores con ≤2 commits | 41 de 84 | calculado |
| Creado / último push | 2023-02-07 / 2026-11-02 00:28 UTC | API de GitHub |
| Licencia | AGPL-3.0 | LICENSE |
| Código por bytes | Zig 11,3 MB, HTML 3,1 MB, Rust 144 KB | API languages |
| Versiones / tags | 1.0.2 (2026-10-23), 1.0.0 (2026-10-02) / 27 tags | API de GitHub |
| Issues abiertas | 99 | API de GitHub |
| Binario que ejecuté | 1.1.0-nightly.11471+cebb8d51e, 77 MB | lightpanda version |
El número de seguidores es el primero que miraría. Las estrellas son un marcador; los seguidores son personas que pidieron que les avisen cuando algo cambie. 98 seguidores sobre 40.121 estrellas es una proporción del 0,24%: para un proyecto que publica resultados WPT a diario y lleva 27 tags, es mucha más gente mirando la idea que el código. La proporción de forks es del 4,9%, normal en infraestructura autoalojada; un navegador que se ejecuta como servicio es algo que se clona, no algo que se estrella y se olvida.
La distribución de commits es más sana que la media: 84 contribuidores, pero los tres primeros concentran el 77,4% de 11.486 commits, y karlseguin solo tiene 4.991. Es un núcleo pequeño con una cola larga de arreglos puntuales: la forma de un proyecto que se está construyendo, no uno que se está manteniendo.
Qué mide en realidad el “9x faster”
El README cita dos números: 123 MB frente a 2 GB de memoria pico (~16x) y 5 s frente a 46 s de ejecución (~9x). El documento de benchmarks que los produce rastrea 933 URLs del sitio de demostración Amiibo del propio proyecto con un crawler en Go sobre chromedp. Estas son las filas de las que sale el titular, más la fila que más importa:
| Ejecución | Duración | Memoria pico | % CPU | Páginas |
|---|---|---|---|---|
| Lightpanda, 1 proceso | 0:51.68 | 27,2 M | 11,1% | 933 |
| Lightpanda, 25 procesos | 0:04.81 | 123,0 M | 204,7% | 933 |
| Lightpanda, 100 procesos | 0:05.23 | 410,2 M | 202,8% | 933 |
| Chrome, 1 pestaña | 1:22.83 | 1,3 G | 124,9% | 933 |
| Chrome, 10 pestañas | 0:45.62 | 1,7 G | 241,6% | 933 |
| Chrome, 25 pestañas | 0:46.70 | 2,0 G | 254% | 933 |
| Chrome, 100 pestañas | 1:09:37 | 4,2 G | 229% | 933 |
El par 123 MB / 5 s frente a 2 GB / 46 s es exactamente la fila 25 contra 25. Es una comparación justa del rendimiento por unidad de hardware, y es la comparación que le importa a quien opera un crawler. Pero no es una comparación de motores, porque los dos lados no hacen lo mismo:
“Lightpanda can’t create multi-tabs. So instead we start 100 browser process, each on it’s own port.”
Esa frase está en el documento de benchmarks y no en la tabla del README. Cambia la interpretación de todas las filas: se comparan 25 procesos de Lightpanda contra 25 pestañas dentro de un único proceso de Chrome. Parte del 9x es el motor, y parte es que a Chrome se le obligó a multiplexar 25 páginas en un solo espacio de direcciones mientras a Lightpanda se le permitió hacer fork.
A igual concurrencia —un proceso contra una pestaña— la misma tabla da 51,68 s frente a 82,83 s (1,6x) y 27,2 MB frente a 1,3 GB (47x). La proporción de memoria con concurrencia 1 es casi tres veces mejor que el titular, y la de velocidad casi seis veces peor. Los dos números son reales; responden a preguntas distintas.
Hay una tercera observación en la columna de Chrome que nadie cita. El rastreo tarda 1:22.83 con una pestaña y 0:45.62 con diez, y ahí se detiene: 0:45.66 con cinco, 0:46.70 con veinticinco. Lo que limite ese rastreo a partir de diez pestañas —idas y vueltas de red, el sitio de demo, el crawler— no es el coste por página de Chrome. Lightpanda sigue mejorando hasta los veinticinco procesos (0:04.81). La proporción del titular es, sobre todo, una afirmación sobre lo barato que resulta añadir concurrencia en Lightpanda, lo cual es una ventaja real, solo que no es la que sugieren las palabras “9x faster than Chromium”.
Tres incoherencias entre las dos páginas que publican el benchmark
Son pequeñas, y juntas hacen que el titular sea difícil de reproducir:
| Afirmación | README | BENCHMARKS.md |
|---|---|---|
| Máquina | “AWS EC2 m5.large instance” | “AWS m5.xlarge (x86_64) with a fresh Ubuntu install” |
| Carga | “Memory (peak, 100 pages)” / “Execution time (100 pages)” | ambas filas van etiquetadas con 933 páginas |
| Chrome 100 pestañas | — | 1:09:37 en la tabla, mientras que la salida de /usr/bin/time -v impresa justo encima dice Elapsed … 1:09.37 |
Una m5.large tiene 2 vCPU y 8 GB; una m5.xlarge, 4 vCPU y 16 GB. La propia columna de CPU muestra un 204,7% y un 254% de utilización a 25 de concurrencia, algo que no cabe en una máquina de 2 vCPU: el benchmark se ejecutó casi con seguridad en la instancia grande y el m5.large del README está desactualizado. Y 1:09:37 es un valor m:ss.ss cuyo separador decimal se convirtió en dos puntos: leído literalmente dice que Chrome necesitó 1 hora y 9 minutos, o sea 83 veces su propia fila de 25 pestañas. El número real es 69,4 s.
Si vas a dimensionar infraestructura a partir del README, la etiqueta “100 pages” es la que duele: las filas que hay detrás rastrearon 933 páginas, así que la memoria por página es de unos 0,13 MB en la ejecución real, no los 1,23 MB que implica la etiqueta.
Diez páginas reales, medidas en esta máquina
Ejecuté el binario nightly sobre diez páginas que representan lo que un agente lee de verdad —HTML estático, un foro renderizado en servidor, una documentación en Next.js, GitHub, Wikipedia, la portada de Python— con lightpanda fetch --dump markdown, y las mismas diez en Chromium vía Playwright con waitUntil: networkidle y un tope de 45 s. En serie, de una en una, en una máquina de 2 vCPU y 7 GB de RAM.
| URL | Lightpanda | Chromium | Nodos en Chromium |
|---|---|---|---|
| example.com | 0,118 s | 0,569 s | 22 |
| news.ycombinator.com | 0,378 s | 0,929 s | 818 |
| news.ycombinator.com/item (538 comentarios) | 0,757 s | 1,293 s | 17.911 |
| lightpanda.io | 0,676 s | 1,523 s | 890 |
| demo-browser.lightpanda.io/amiibo | 0,184 s | 0,958 s | 39 |
| github.com/trending | 1,427 s | 2,793 s | 3.588 |
| react.dev | 0,555 s | 1,196 s | 1.846 |
| python.org | 0,531 s | 1,007 s | 902 |
| docs.python.org asyncio-task | 0,425 s | 0,798 s | 4.187 |
| en.wikipedia.org Headless_browser | 0,489 s | 0,983 s | 1.322 |
| total | 5,54 s | 12,75 s | — |
| RSS pico | ~70,5 MB (un proceso) | 599,6 MB (árbol de procesos) | — |
Eso es 2,3 veces más rápido y 8,5 veces más ligero en esta carga: ventajas reales, muy por debajo de 16x y 9x, y tampoco una contradicción del benchmark, porque usé concurrencia 1 contra concurrencia 1 y un conjunto de páginas variado en lugar de un solo sitio de demostración. Las salvedades son mías: los dos conjuntos corrieron con quince minutos de diferencia, así que la red se movió; no limité la tasa de ninguno de los dos; y Lightpanda pagó el arranque de proceso diez veces porque cada fetch es un proceso nuevo.
Los dos lados acertaron en las diez URLs con código de salida 0. La fidelidad del texto es lo que merece una segunda mirada, y tiene su propia sección más abajo.
Leer el JSON crudo de WPT en vez del contador de aprobados
El panel de perf.lightpanda.io/wpt muestra PASS: 1841066, FAIL: 283283, CRASH: 0 para la ejecución del 1 de noviembre de 2026 a las 04:51 UTC, sobre el commit cebb8d51e, que es exactamente el commit del binario que descargué. La página solo pinta unas decenas de filas de rutas, pero el navegador se descarga un JSON de 218 MB por detrás, y ese fichero tiene cada subtest. Lo agregué.
Los totales cuadran exactamente: 1.841.066 aprobados de 2.124.349 subtests, 86,66%.
Ahora la composición, que es la parte que cambia cómo se lee el titular:
| Ruta | Subtests aprobados | Total | Tasa | Porcentaje de todos los aprobados |
|---|---|---|---|---|
encoding | 1.151.555 | 1.152.031 | 100,0% | 62,5% |
wasm | 191.425 | 191.957 | 99,7% | 10,4% |
html | 129.360 | 155.081 | 83,4% | 7,0% |
editing | 68.762 | 114.840 | 59,9% | 3,7% |
dom | 62.040 | 65.865 | 94,2% | 3,4% |
css | 56.597 | 177.275 | 31,9% | 3,1% |
webcryptoapi | 55.523 | 70.664 | 78,6% | 3,0% |
encoding es la suite de Web Platform Tests de tablas de codificación de caracteres: decenas de miles de aserciones generadas del tipo “el byte 0x82 en Shift_JIS corresponde a este punto de código”. El post de la 1.0 del propio proyecto es honesto al respecto, y es el párrafo más útil de sus notas de versión:
“April 2026: the encoding/ suite started passing. It holds about 1.15 million subtests, mostly large generated tables of character mappings, so the count jumped in a few days.”
Así que el salto de ~290.000 a ~1,7 millones de aprobados fue una sola suite. Quitando encoding y wasm quedan 498.086 subtests aprobados, el 27% del total, y ese es el número que de verdad corresponde a “características del navegador que una página puede llamar”. Para situarlo: el post de la 1.0 atribuye a Chrome 2.184.491 subtests en wpt.fyi, así que los 1.841.066 de Lightpanda son el 84,3% del recuento de Chrome, pero la composición no se parece en nada a la de Chrome, donde css por sí sola es una porción grande tanto de aprobados como de fallados.
Hay dos hechos más que un único contador aprobado/fallado esconde:
- Solo 11.626 de 38.199 ficheros de test (30,4%) pasan por completo. Siete de cada diez ficheros WPT tienen al menos un subtest fallado. Un contador dominado por dos suites enormes no lo muestra.
cssconcentra 120.656 de los 283.283 fallos: el 42,6% de todos los subtests fallados. Ese es el coste directo de no tener motor de layout ni de pintado, y el proyecto lo dice: “More than half of the subtests Chrome passes and Lightpanda doesn’t are in the CSS, editing and SVG suites, which test layout and painting.”
Las suites que deciden si una página concreta funcionará son más pequeñas y mucho más informativas que el total:
| Suite | Tasa de aprobados | Qué se rompe cuando falla |
|---|---|---|
dom | 94,2% | Manipulación de elementos |
cors | 90,9% (421/463) | fetch entre orígenes; en el post de la 1.0 era 371/463 |
cookies | 88,0% | Sesiones |
indexeddb | 88,3% | Almacenamiento de cliente |
html | 83,4% | Parseo, formularios, elementos multimedia |
xhr | 79,1% | Ajax antiguo |
fetch | 70,0% | Ajax moderno |
websockets | 69,3% | Sockets en vivo |
workers | 67,2% | Todo lo que corre en un worker |
content-security-policy | 46,1% | Páginas que fijan CSP |
intersection-observer | 40,1% | Contenido con carga diferida |
service-workers | 38,9% | Aplicaciones que funcionan sin conexión |
streams | 32,0% | Cuerpos de respuesta en streaming |
clipboard-apis | 11,7% | Botones de copiar |
Si tu sitio objetivo carga imágenes de forma diferida con IntersectionObserver (40%), usa un service worker (39%) o transmite un cuerpo de respuesta (32%), esos porcentajes son los que hay que leer antes que el 86,66% del titular. Que --load-resources sea opcional para iframes, workers y hojas de estilo externas multiplica el efecto: por defecto, el código de worker de una página ni siquiera se ejecuta.
Una página por proceso de navegador
Esta es la restricción que decidirá si Lightpanda encaja en un pipeline existente, así que la probé como lo haría un usuario de Playwright.
playwright.chromium.connect_over_cdp("http://127.0.0.1:9222") funciona: el navegador se declara como versión 124.0.6367.29 y user agent Lightpanda/1.0. Después:
Browser.new_context: Protocol error (Target.createBrowserContext):
Cannot have more than one browser context at a time
BrowserContext.new_page (segunda página): Protocol error (Target.createTarget):
TargetAlreadyLoaded
Page.goto en la página que te dan al conectar:
Protocol error (Page.navigate):
No page on the STARTUP session; create a real target with Target.createTarget
Así que la forma que funciona es exactamente una secuencia: conectar, new_context(), new_page(), goto(). Una segunda página en el mismo contexto falla. Un segundo contexto falla. La página que existe antes de crear el contexto no puede navegar.
Esto es coherente con la frase tajante del documento de benchmarks, que Lightpanda “can’t create multi-tabs”, y explica la fila de 100 procesos. También significa que el diseño habitual de crawler —un navegador, N páginas, un pool de workers sobre pestañas— no se traslada. La paralelización se consigue con N procesos, cada uno en su puerto, igual que hace el modo --fork del benchmark. La alternativa dentro del mismo proceso es el transporte HTTP de MCP, donde cada Mcp-Session-Id recibe su propia sesión de navegación; es una superficie de API distinta de CDP.
Para un agente de una sola página que navega un sitio a la vez, nada de esto importa. Para un scraper con un pool de pestañas, es una reescritura, y es la clase de reescritura que conviene descubrir el primer día y no en la tercera semana.
Tres cosas que el volcado de texto hace distinto a un navegador
El producto de Lightpanda para un agente es texto: --dump markdown, --dump semantic_tree_text, --dump structured_data. Tres diferencias medidas frente al innerText de Chromium:
1. El aviso de “tus scripts no se ejecutaron” de la propia página sobrevive. En python.org, el markdown de Lightpanda contiene el bloque oculto #nojs tal cual —“This page displays a fallback because interactive scripts did not run”— y getComputedStyle sobre ese elemento devuelve display: block. Chromium lo esconde. En el log del propio Lightpanda se ve que las tres URLs de script de la página se ejecutan, que tres XHR devuelven 200, y que el mismo log registra not_implemented feature=webgl. Por qué la página sigue creyendo que sus scripts fallaron no lo documenta ninguna de las dos partes; lo que sí está documentado es que Lightpanda informa las características no soportadas en nivel info, donde nadie mira.
2. El texto con visibility:hidden se vuelca. Serví una página con un marcador visible y ocho técnicas de ocultación y comparé los dos navegadores:
| Técnica de ocultación | Markdown de Lightpanda | innerText de Chromium |
|---|---|---|
style="display:none" | excluido | excluido |
display:none desde hoja de estilo | excluido | excluido |
aria-hidden="true" | excluido | excluido |
atributo hidden | excluido | excluido |
contenido de <template> | excluido | excluido |
contenido de <noscript> | excluido | excluido |
visibility:hidden | incluido | excluido |
position:absolute; left:-9999px | incluido | incluido |
Siete de ocho coinciden. El que no coincide es visibility:hidden, la técnica que usan los sitios para etiquetas accesibles, enlaces de salto y, de vez en cuando, cosas que preferirían que un scraper no leyera.
3. robots.txt es opcional, y las páginas de bloqueo llegan en base64. La bandera es --obey-robots; sin ella no se comprueba nada. Pedí un hilo de Reddit de las dos maneras. Con la bandera: Navigation failed … Reason: RobotsBlocked, que es el comportamiento correcto. Sin ella: la página de bloqueo de Reddit, cuyo data:image/png;base64 en línea acabó volcado literalmente en el markdown: decenas de kilobytes de base64 en un solo documento. --dump-max-bytes existe justo para esto, y si vas a pasar la salida a un modelo, lo querrás.
Dos detalles menores encontrados del mismo modo: las URLs file:// fallan con UnsupportedProtocol, así que los fixtures HTML locales hay que servirlos por HTTP; y --dump markdown primero pide markdown al servidor por negociación de contenido (“markdown served” en el log), lo que significa que en sitios que sirven markdown a agentes la salida puede no ser el DOM de la página. Es útil, y conviene saberlo cuando un diff sale raro.
Una receta que respeta la restricción
Todo lo siguiente corrió en la máquina de 2 vCPU, contra el nightly del 2 de noviembre.
# 1. Instalación (glibc 2.35+ en x86_64; 2.38+ en arm64 — Alpine no sirve)
curl -L -o lightpanda \
https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-linux
chmod a+x ./lightpanda
./lightpanda version # 1.1.0-nightly.11471+cebb8d51e
# 2. Una página, texto fuera — la llamada útil más barata
./lightpanda fetch --dump markdown --log-level error https://example.com/
# 3. Servidor CDP para Playwright/Puppeteer
./lightpanda serve --host 127.0.0.1 --port 9222 --log-level info
// 4. Playwright: un contexto, una página, y reutilizar esa página para cada URL
const { chromium } = require('playwright');
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222');
const context = await browser.newContext(); // solo uno, nunca más
const page = await context.newPage(); // y solo una
for (const url of urls) {
await page.goto(url, { waitUntil: 'domcontentloaded' });
const text = await page.evaluate(() => document.body.innerText);
}
# 5. Paralelismo: N procesos en N puertos, no N pestañas
for p in 9222 9223 9224 9225; do
./lightpanda serve --host 127.0.0.1 --port $p --log-level warn &
done
Dos banderas que conviene adoptar antes de la primera ejecución en producción: --obey-robots y LIGHTPANDA_DISABLE_TELEMETRY=true; la telemetría está activada por defecto, y el README lo dice en una línea.
Preguntas frecuentes
¿Lightpanda es un fork de Chromium?
No. Está escrito en Zig, usa V8 para JavaScript, html5ever para parsear HTML y libcurl para HTTP, sin Blink y sin motor de renderizado. De ahí vienen tanto su ventaja de memoria como su hueco en CSS: aprueba el 31,9% de la suite css, y los fallos de CSS son el 42,6% de todos sus subtests fallados.
¿Cuántos subtests WPT aprueba realmente?
1.841.066 de 2.124.349 en la ejecución del 1 de noviembre de 2026: 86,66%, frente a los 2.184.491 de Chrome según la comparación del propio proyecto. De esos aprobados, 1.151.555 (62,5%) vienen de la suite encoding y 191.425 (10,4%) de wasm; quitando ambas quedan 498.086 subtests de cobertura de características, y solo el 30,4% de los ficheros de test pasan por completo.
¿Puedo abrir varias páginas en una misma instancia?
Por CDP, con el nightly del 2 de noviembre, no. Un segundo new_page() en el mismo contexto lanza TargetAlreadyLoaded; un segundo new_context() lanza Cannot have more than one browser context at a time. El paralelismo significa un proceso por puerto, que es lo que hace el propio benchmark del proyecto.
¿Hace capturas de pantalla?
No hay pipeline de renderizado. --dump png y --dump pdf convierten el markdown de la página en una imagen, lo cual sirve para logs y no sirve para verificar maquetación. El post de la 1.0 dice explícitamente que su propio producto de agente recurre a Chrome para las capturas.
¿Lo bloquearán los sistemas anti-bot? El fingerprinting se apoya justo en lo que Lightpanda omite: salida de canvas y WebGL, enumeración de fuentes, métricas de layout. Los mantenedores dijeron en enero de 2025 que no habían trabajado en detección de bots, y nada en la documentación actual afirma lo contrario. Para sitios protegidos, cuenta con Chrome.
¿Puedo usarlo en un producto comercial? Es AGPL-3.0, con CLA para contribuciones, y los fundadores han dicho que la licencia se eligió para que una empresa que ofrezca el navegador como servicio gestionado tenga que publicar sus modificaciones. Manejarlo a través de un proceso o de la frontera CDP es una pregunta distinta de incrustarlo en tu binario, y es una pregunta para tu abogado, no para este artículo.
Veredicto
Lightpanda está haciendo algo que ningún otro proyecto hace: un navegador que ejecuta el JavaScript de la web moderna en 70 MB, dentro de un binario de 77 MB, un proceso por página, con 118 ms para traer una página trivial. Para un agente que debe leer páginas de contenido en una VM pequeña, es una ventaja real y medible en este hardware: 2,3 veces menos tiempo de reloj y 8,5 veces menos memoria, con concurrencia 1, y con todas las páginas saliendo bien.
Las afirmaciones que lo rodean son más flojas que la ingeniería. “9x faster” es 1,6x a igual concurrencia; “16x lighter” es 47x a igual concurrencia y 16x con veinticinco procesos; “1,7 million WPT subtests” es el 27% de esa cifra en características de navegador cuando se apartan dos suites generadas. La página del benchmark se contradice en tipo de instancia y número de páginas, y su celda de Chrome con 100 pestañas tiene una errata que convierte 69 segundos en una hora. Nada de eso es deshonesto —cada cifra se remonta a una fila real— pero la composición nunca está en el titular, y la composición es lo que decide si tu página se renderiza.
El resumen honesto es más estrecho que el marketing y más útil: Lightpanda es un navegador rápido, barato en memoria, de una página por proceso, que ejecuta bien JavaScript, no hace layout en absoluto y nunca hace multipestaña. Si tus páginas objetivo son contenido y formularios, quédate con él. Si son maquetación, widgets de carga diferida, workers o captchas, mide las suites concretas de arriba antes de migrar, y prueba tus propias diez páginas primero, porque la diferencia entre el 86,66% y tu página es una página.