En junio de 2026, Deno lanzó oficialmente deno desktop en su versión v2.9.0 — un comando que empaqueta cualquier proyecto TypeScript (un script de un solo archivo, una aplicación Next.js, o incluso un servidor HTTP) en un .app de macOS, un .exe de Windows y un AppImage de Linux. Consiguió 997 puntos y 365 comentarios en HN, y △34 en Lobsters. La puntuación no se debe solo a la popularidad de Deno — la discusión más intensa en los comentarios se centró en una decisión técnica que Deno tomó: empaquetar CEF (Chromium Embedded Framework) por defecto, en lugar de depender del WebView del sistema como hace Tauri.
Esta decisión cosechó tanto aplausos como críticas en la comunidad. Los aplausos vinieron de quienes han sufrido con webkitgtk en Linux; las críticas, de quienes piensan «¿por qué no usar el motor de navegador que ya trae el sistema?». Ambas voces tienen razón, y la elección de Deno se sitúa justo en medio de ambas.
La maldición de los 200 MB de Electron
La razón por la que Electron domina el desarrollo de aplicaciones de escritorio es simple: escribes la UI con HTML/CSS/JS, llamas a APIs del sistema con Node.js, y un mismo código funciona en Windows/macOS/Linux. El costo también es simple: cada aplicación Electron es una copia independiente del navegador Chromium, más el runtime de Node.js, con un peso inicial de 150 MB que puede llegar a 250 MB. Slack, VS Code, Discord, Figma — puedes tener cinco aplicaciones Electron en tu disco duro, lo que significa que tienes cinco copias de Chromium.
Esto no es solo un desperdicio de espacio en disco. Cada aplicación Electron inicia su propio conjunto de procesos del navegador — proceso GPU, proceso de renderizado, proceso de red — y el uso de memoria se acumula linealmente. Una pestaña de Chrome consume unos 100 MB de memoria; tres aplicaciones Electron ejecutándose simultáneamente pueden consumir fácilmente más de 1.5 GB. El usuario percibe «¿por qué mi aplicación de notas consume más memoria que mi IDE?», cuando la verdad es que tu aplicación de notas es un anfitrión de navegador a nivel de IDE que ejecuta un <textarea>.
El equipo de Electron es consciente del problema. Experimentaron con electron-shared-library para permitir que múltiples aplicaciones Electron compartieran una misma copia de las bibliotecas dinámicas de Chromium, pero nunca se materializó. El obstáculo fundamental no es técnico — es el infierno de dependencias de versiones entre aplicaciones. La aplicación A depende de Electron 28, la aplicación B depende de Electron 31; la compatibilidad ABI de las bibliotecas compartidas es casi imposible de mantener con la frecuencia de actualización de Chromium (una versión mayor cada cuatro semanas). Los gestores de paquetes de las distribuciones Linux lo resuelven fijando toda la distribución a una instantánea de versión, pero las aplicaciones de escritorio no tienen ese lujo — no puedes exigir que un usuario actualice VS Code a la fuerza solo porque Discord se ha actualizado.
La ruta del WebView del sistema de Tauri: idea correcta, práctica cruel
Tauri tomó otro camino. Su idea central es: si el sistema operativo ya incluye un motor de navegador, ¿por qué distribuir otro? macOS tiene WKWebView, Windows tiene WebView2, Linux tiene webkit2gtk. El binario de Tauri es extremadamente pequeño — menos de 10 MB al inicio — porque el motor de renderizado está completamente externalizado al sistema operativo. El backend está escrito en Rust, el frontend puede ser cualquier framework JS, y la IPC se realiza a través del bridge personalizado de Tauri.
Esta idea funciona bien en Windows. WebView2 está basado en Edge Chromium, Microsoft lo actualiza a través de Windows Update, la versión es relativamente moderna y la compatibilidad es buena. El problema está en macOS y Linux.
En macOS, WKWebView está vinculado a la versión del sistema operativo. Esto significa que si tu usuario sigue en macOS 13, tu aplicación Tauri ejecutará la versión de WebKit correspondiente a macOS 13 — que puede estar dos o tres versiones mayores por detrás del Safari más reciente. Las nuevas características de CSS no son compatibles, las nuevas APIs Web no están expuestas, ciertos comportamientos de Canvas/WebGL no coinciden con los de Chrome. Los desarrolladores de Tauri no pueden hacer nada al respecto — no tienen capacidad ni voluntad para reemplazar el WebKit del sistema en la máquina del usuario. El ritmo de actualización de WKWebView de Apple está completamente fuera del control del desarrollador de aplicaciones.
La situación en Linux es peor. Linux no tiene el concepto de «motor de navegador del sistema» — diferentes entornos de escritorio, diferentes versiones de GTK, diferentes distribuciones empaquetan versiones distintas de webkit2gtk. En los comentarios de HN sobre Deno Desktop, un desarrollador veterano de Tauri, echelon, escribió una evaluación muy citada: webkitgtk es «lento y devora memoria». No es una queja personal — los issues #3988 y #7021 de Tauri en GitHub documentan graves degradaciones de rendimiento de webkit2gtk en Linux con grandes cantidades de elementos DOM, incluyendo ralentizaciones en el scroll, caídas de frames en el renderizado y regresiones de rendimiento conocidas introducidas en WebKit 2.40.
El verdadero problema de Tauri en Linux es que no hay un motor de renderizado fiable disponible. webkit2gtk es mantenido por la comunidad WebKitGTK, cuyos recursos de desarrollo son mucho menores que los del equipo de Chromium — Chromium cuenta con ingenieros a tiempo completo de Google e investigadores de seguridad; los mantenedores principales de WebKitGTK se cuentan con los dedos de una mano. Esto no es menospreciar la capacidad de los desarrolladores de WebKitGTK — hacen un trabajo admirable — pero la comparación de fuerzas es un hecho objetivo.
La elección de Deno: empaquetar CEF, pero no en exclusiva
Deno Desktop eligió un tercer camino. Por defecto usa CEF (Chromium Embedded Framework) — igual que Electron, basado en Chromium, pero con dos diferencias clave.
Primero, CEF es solo el motor del navegador, sin Node.js. El paquete de Electron incluye tanto el motor de renderizado Chromium como el runtime Node.js, profundamente acoplados a través de libnode. La arquitectura de Deno Desktop es diferente: el propio Deno es el runtime JS/TS (basado en V8), y CEF solo se encarga de renderizar páginas frontend HTML/CSS/JS. El proceso de Deno no se ejecuta dentro de CEF — se ejecuta como un proceso independiente que inicia un servidor HTTP local, y la ventana de CEF carga http://localhost:<port> para renderizar la UI. La comunicación frontend-backend se realiza mediante HTTP/WebSocket ordinario, no a través del puente intraproceso ipcMain/ipcRenderer de Electron.
Una consecuencia directa de esta elección arquitectónica es: las aplicaciones Deno Desktop pueden cambiar a otros backend de renderizado. Deno soporta tres backends: cef (por defecto), webview (WebView del sistema) y winit (ventanas Rust puras, adecuadas para juegos/aplicaciones gráficas). CEF es la opción recomendada por defecto, pero si no te importa la compatibilidad, puedes cambiar a webview para disfrutar de un tamaño binario más pequeño. Esta flexibilidad no la tiene Electron — el acoplamiento de Electron con Chromium es demasiado profundo para «desconectarlo».
Segundo, la hoja de ruta pública de Deno incluye un punto explícito: runtime CEF compartido. Actualmente, cada aplicación Deno Desktop sigue empaquetando su propia copia de las bibliotecas dinámicas de CEF, pero el equipo de Deno planea implementar en el futuro un «runtime compartido gestionado» — múltiples aplicaciones Deno Desktop compartiendo una sola instalación de CEF en la máquina. Esta línea sigue la misma dirección que el experimento shared-library que Electron intentó y abandonó, pero Deno tiene una ventaja que Electron no tiene: todas las aplicaciones Deno Desktop se ejecutan bajo un mismo marco de gestión de versiones de runtime. El mecanismo de actualización de versiones de Deno puede garantizar que «si tienes dos aplicaciones Deno Desktop instaladas, la versión de CEF que usan es gestionada centralizadamente por Deno», de forma similar a cómo un gestor de paquetes a nivel de sistema gestiona las versiones de bibliotecas compartidas. No es un problema resuelto — los puntos en una hoja de ruta no son entregas — pero la dirección es correcta.
Características técnicas de CEF
CEF en sí mismo es un proyecto de madurez extremadamente alta. El cliente de escritorio de Spotify, algunos componentes de Adobe Creative Cloud, el Epic Games Launcher, las fuentes de navegador de OBS Studio — todos usan CEF para incrustar Chromium. Su arquitectura multiproceso es idéntica a la de Chrome: un proceso browser gestiona ventanas y red, cada instancia de página se ejecuta en un proceso renderer independiente, y el proceso GPU se encarga de la composición y la aceleración por hardware. El aislamiento que proporciona esta arquitectura complementa perfectamente el modelo de seguridad de Deno — Deno deniega por defecto el acceso al sistema de archivos, la red y las variables de entorno, y el proceso renderer en sandbox de CEF reduce aún más la superficie de ataque del código frontend.
CEF también admite renderizado fuera de pantalla (Off-Screen Rendering, OSR). En modo normal, CEF crea una ventana nativa y renderiza en ella; en modo OSR, el resultado del renderizado se envía a un búfer de memoria que la aplicación anfitriona decide cómo mostrar. Esta capacidad es importante para el backend winit de Deno Desktop — si en el futuro Deno quiere soportar frameworks de UI completamente personalizados (como UI impulsada por GPU), el modo OSR de CEF puede alimentar directamente el contenido web como textura en el pipeline de renderizado.
Pero CEF no está exento de costos. Una biblioteca dinámica de CEF (libcef.so) ocupa unos 150 MB, más los archivos de recursos de Chromium (.pak, icudtl.dat, locales), el consumo total de disco ronda los 200 MB. Esto es similar a Electron. Solo mirando el tamaño del binario, Deno Desktop + CEF no es más ligero que Electron — su ventaja no está ahí. Está en dos puntos: primero, CEF se puede compartir, mientras que el acoplamiento Node.js+Chromium de Electron es difícil de compartir; segundo, Deno te permite usar el backend webview para buscar el mínimo volumen, una opción que Electron no tiene.
Comparativa de las tres soluciones
Poner las tres soluciones en una misma tabla clarifica sus respectivas compensaciones.
| Dimensión | Electron | Tauri | Deno Desktop (CEF) |
|---|---|---|---|
| Motor de renderizado | Chromium empaquetado | WebView del sistema | CEF empaquetado (se puede cambiar a WebView del sistema) |
| Lenguaje backend | Node.js (JS) | Rust | Deno (JS/TS) |
| Tamaño binario | 150-250 MB | 3-15 MB | 200 MB (modo CEF) / 15 MB (modo webview) |
| Compatibilidad macOS | Chromium más reciente, sin restricciones del SO | Limitado a la versión de WKWebView del SO | CEF más reciente, sin restricciones del SO |
| Compatibilidad Linux | Consistente | Depende de webkit2gtk, rendimiento y compatibilidad variables | Consistente (CEF propio) |
| Modelo de procesos | Main + Renderer (Node.js y Chromium fuertemente acoplados) | Proceso principal Rust + proceso WebView del sistema | Proceso servidor HTTP Deno + procesos browser/renderer de CEF |
| Potencial de motor compartido | Bajo (fragmentación severa de versiones) | Compartido naturalmente (usa el motor del sistema) | Medio (plan de runtime compartido en la hoja de ruta) |
| Soporte de frameworks frontend | Cualquier framework JS | Cualquier framework JS | Cualquier framework JS (incluyendo frameworks full-stack como Next.js) |
| Mecanismo de actualización | Requiere implementación propia | Requiere implementación propia | Integrado (actualización en caliente al estilo Deno Deploy) |
La fila más fácil de malinterpretar en esta tabla es «tamaño binario». Los 200 MB de Deno Desktop en modo CEF parecen igual de malos que los de Electron, pero la diferencia clave está en qué parte de esos 200 MB es «código variable» y qué parte es «motor compartible». De los 200 MB de Electron, Chromium + Node.js ocupan unos 180 MB, y cada aplicación los empaqueta de forma independiente. De los 200 MB de Deno Desktop, CEF también ocupa unos 150 MB, pero la hoja de ruta del runtime compartido de Deno significa que esta parte podría almacenarse solo una vez en el futuro. Hasta que el runtime compartido se materialice, Deno Desktop no gana a Electron en tamaño; después de que se materialice, tiene el potencial de reducir el tamaño incremental de cada aplicación a un solo dígito de MB — como hace Tauri hoy — pero sin sacrificar la consistencia del motor de renderizado.
Dependencias compartidas: una sabiduría de Linux que las aplicaciones de escritorio han olvidado
Las distribuciones de Linux llevan treinta años resolviendo el problema de las dependencias compartidas con gestores de paquetes. libssl.so, libgtk.so, libc.so — siempre hay solo una copia en el sistema, y todas las aplicaciones enlazan a ella. Las actualizaciones de versión son coordinadas por el gestor de paquetes, y la compatibilidad ABI se garantiza a nivel de distribución. Este mecanismo funciona tan bien que los usuarios de Linux sienten un rechazo natural hacia que «cada aplicación traiga su propia copia de OpenSSL».
¿Por qué las aplicaciones de escritorio reinventan esta rueda? La raíz está en la diferencia de modelos de confianza, no en inmadurez técnica. El gestor de paquetes de Linux funciona porque existe una autoridad central (los mantenedores de la distribución) responsable de la coherencia de versiones de todos los paquetes. El ecosistema de aplicaciones de escritorio no tiene esa autoridad central — VS Code lo publica Microsoft, Discord lo publica Discord Inc., Figma lo publica Figma Inc., y no hay ningún mecanismo de coordinación entre ellos. Cada desarrollador de aplicaciones solo puede asumir «no sé qué hay en la máquina del usuario», y elige la estrategia más conservadora: empaquetar todo lo que necesito.
El plan de runtime CEF compartido de Deno Desktop intenta encontrar un punto intermedio entre estos dos extremos: no un compartir全局 a nivel de sistema (que requeriría coordinación a nivel de SO), sino un compartir gestionado dentro del ecosistema Deno. Todas las aplicaciones construidas y distribuidas con deno desktop tendrían su versión de CEF controlada por el gestor de versiones unificado de Deno. Esto se asemeja al mecanismo de runtime de Flatpak — múltiples aplicaciones Flatpak comparten un runtime de KDE/GNOME — pero con un grano más fino, compartiendo solo el motor del navegador.
El éxito de este camino depende de dos variables: primero, si el ecosistema de Deno Desktop puede generar suficientes aplicaciones para que el uso compartido tenga sentido (si solo hay tres aplicaciones Deno Desktop, el beneficio del runtime compartido es mínimo); segundo, si la estabilidad ABI de CEF puede soportar la situación de «múltiples aplicaciones dependiendo de la misma copia de CEF pero con diferentes frecuencias de actualización». La estabilidad de la API de CEF es mejor que la del propio Chromium — la capa wrapper de la API de CEF proporciona mucha amortiguación — pero no es monolítica. Cuando CEF hace una actualización de versión mayor, cómo maneja el gestor de runtime compartido el caso de «la aplicación A es compatible con la nueva versión, la aplicación B solo es compatible con la anterior» es algo para lo que el equipo de Deno aún no ha publicado una solución técnica.
El factor decisivo de esta apuesta
echelon señaló en HN por qué la elección de CEF por parte de Deno es la dirección correcta: la experiencia con el WebView del sistema de Tauri en macOS y Linux es demasiado dolorosa. La idea de Tauri es limpia — usar lo nativo del sistema, no distribuir binarios duplicados — pero la realidad es que el ritmo de actualización de WKWebView en macOS lo decide Apple, y la calidad de webkit2gtk en Linux la garantiza una pequeña comunidad de código abierto. La limpieza de la idea no compensa la rudeza de la implementación — el usuario solo recordará «esta aplicación va tan lenta en Linux que no se puede usar», no le importará «esta aplicación usa el WebView del sistema, por eso ahorra espacio».
La ruta CEF de Deno sacrifica la pureza de la idea a cambio de control sobre la implementación. Reconoce un hecho incómodo pero real de la ingeniería: en el ámbito de las aplicaciones de escritorio multiplataforma, el control es más importante que el tamaño. Si un motor de renderizado se comporta de forma inconsistente en plataformas que no controlas, el espacio de disco ahorrado se ve multiplicado por el tiempo de desarrollo consumido en problemas de compatibilidad.
Pero Deno tampoco ha renunciado a la optimización del tamaño — la hoja de ruta del runtime compartido es su diferencia fundamental con Electron. Si el runtime CEF compartido se materializa con éxito, Deno Desktop tendrá simultáneamente «consistencia del motor de renderizado» (del CEF empaquetado) e «incremento de tamaño pequeño» (de la arquitectura compartida), algo que ni Electron (solo consistencia, sin tamaño pequeño) ni Tauri (solo tamaño pequeño en Windows, sin consistencia en macOS/Linux) han logrado individualmente.
Por supuesto, lo que está en la hoja de ruta no puede evaluarse como si ya estuviera entregado. Deno Desktop sigue siendo una versión canary, su API no es estable, y el runtime compartido es solo un «plan futuro». En este ámbito nunca faltan hermosos diagramas de arquitectura — lo que falta son equipos que puedan llevar la gestión de versiones de motores compartidos, un problema aparentemente simple pero en realidad endiabladamente difícil, a una implementación de ingeniería real. El equipo de Deno tiene la capacidad para ello (la gestión de versiones de Deno y su sistema de caché de módulos remotos son una infraestructura de la que se puede aprender), pero la distancia entre la capacidad y la entrega es la apuesta en sí misma.