The CEF Bet: Deno Desktop's Middle Path

The CEF Bet: Deno Desktop's Middle Path

DenoDesktopCEFElectronTauri

Sources:HN + Lobsters · HN

In June 2026, Deno officially released deno desktop in v2.9.0 — a single command that packages any TypeScript project (single-file scripts, Next.js applications, even an HTTP server) into macOS .app, Windows .exe, and Linux AppImage bundles. It scored 997 points and 365 comments on HN, and △34 on Lobsters. The high score wasn’t just about Deno’s name recognition — the most heated discussion centered on a specific technical choice: bundling CEF (Chromium Embedded Framework) by default, rather than relying on the system WebView like Tauri does.

This choice drew both applause and skepticism from the community. Applause came from developers scarred by webkitgtk on Linux; skepticism from those who wonder “why not use the browser engine that’s already on the system.” Both voices have merit, and Deno’s choice sits precisely between them.

Electron’s 200MB Curse

Electron dominates desktop app development for a simple reason: you write UI in HTML/CSS/JS, call system APIs via Node.js, and run one codebase across Windows/macOS/Linux. The cost is equally simple: every Electron app ships its own copy of the Chromium browser plus the Node.js runtime, starting at 150MB and reaching 250MB. Slack, VS Code, Discord, Figma — you might have five Electron apps on your drive, meaning five copies of Chromium.

This isn’t just disk space waste. Each Electron app launches its own set of browser processes — GPU process, renderer process, network process — with memory usage stacking linearly. A Chrome tab costs about 100MB of memory. Three Electron apps running simultaneously can easily consume over 1.5GB. The user perception is “why does my notes app use more memory than my IDE?” — the truth is your notes app is an IDE-grade browser host, just running a <textarea>.

The Electron team is aware of this. They experimented with electron-shared-library, trying to share one Chromium dynamic library across multiple Electron apps, but it was never shipped. The fundamental obstacle isn’t technical — it’s the per-app version dependency nightmare. App A depends on Electron 28, App B on Electron 31. Chromium ships a major version every four weeks, making shared library ABI compatibility nearly impossible to maintain. Linux distribution package managers solve this by locking the entire distro to a single version snapshot, but desktop apps don’t have that luxury — you can’t force VS Code to upgrade just because Discord updated.

Tauri’s System WebView Path: Right in Theory, Brutal in Practice

Tauri took a different route. Its core insight: since the OS already has a built-in browser engine, why ship another? macOS has WKWebView, Windows has WebView2, Linux has webkit2gtk. Tauri’s binary is tiny — under 10MB — because the rendering engine is entirely outsourced to the OS. The backend is written in Rust, the frontend can be any JS framework, and IPC goes through Tauri’s custom bridge.

This works reasonably well on Windows. WebView2 is based on Edge Chromium, updated via Windows Update, reasonably current and compatible. The problems are on macOS and Linux.

On macOS, WKWebView is tied to the OS version. If your user is still on macOS 13, your Tauri app runs the WebKit version corresponding to macOS 13 — potentially two to three major versions behind the latest Safari. New CSS features unsupported. New Web APIs unavailable. Certain Canvas/WebGL behavior inconsistent with Chrome. Tauri developers can do nothing about this — they can’t and won’t replace the system WebKit on user machines. Apple’s WKWebView update cadence is entirely outside the application developer’s control.

Linux is worse. Linux has no concept of a “system browser engine” — different desktop environments, different GTK versions, different distros packaging different webkit2gtk versions. In the Deno Desktop HN discussion, a long-time Tauri developer echelon wrote a frequently cited assessment: webkitgtk is “slow and memory-hungry.” This isn’t a personal complaint — Tauri GitHub Issues #3988 and #7021 document severe performance degradation with webkit2gtk on Linux under heavy DOM element scenarios, including scroll jank, dropped rendering frames, and known performance regressions introduced in WebKit 2.40.

Tauri’s real problem on Linux: there simply isn’t a reliable rendering engine to choose. webkit2gtk is maintained by the WebKitGTK community, with far fewer development resources than the Chromium team — Chromium has full-time Google engineers and security researchers, while WebKitGTK’s core maintainers can be counted on one hand. This isn’t to diminish the WebKitGTK developers’ capabilities — they do admirable work — but the force ratio is an objective fact.

Deno’s Choice: Bundle CEF, But Don’t Lock In

Deno Desktop chose a third path. It uses CEF (Chromium Embedded Framework) by default — based on Chromium like Electron, but with two key differences.

First, CEF is a pure browser engine, without Node.js. Electron’s bundle contains both the Chromium rendering engine and Node.js runtime, deeply coupled through libnode. Deno Desktop’s architecture is different: Deno itself is the JS/TS runtime (based on V8), and CEF only handles rendering HTML/CSS/JS frontend pages. The Deno process doesn’t run inside CEF — it starts a local HTTP server as a separate process, and the CEF window loads http://localhost:<port> to render the UI. Frontend-backend communication goes over regular HTTP/WebSocket, not an in-process bridge like Electron’s ipcMain/ipcRenderer.

A direct consequence of this architecture: Deno Desktop apps can switch to other rendering backends. Deno supports three backends: cef (default), webview (system WebView), and winit (pure Rust window, suitable for games/graphics apps). CEF is the recommended default, but if you don’t care about compatibility, you can switch to webview for a much smaller binary. This flexibility is something Electron doesn’t have — Electron’s Chromium binding is too deep to “switch out.”

Second, Deno’s public roadmap explicitly includes a shared CEF runtime. Currently each Deno Desktop app still bundles its own copy of the CEF dynamic library, but the Deno team plans to implement a “managed shared runtime” — multiple Deno Desktop apps sharing one CEF installation on the machine. This direction is similar to the shared-library experiment Electron tried and abandoned, but Deno has an advantage Electron doesn’t: all Deno Desktop apps operate under the same runtime version management framework. Deno’s version update mechanism can ensure that “if you have two Deno Desktop apps on your machine, the CEF version they use is managed uniformly by Deno” — similar to how a system package manager manages shared library versions. This isn’t a solved problem yet — roadmap items aren’t deliverables — but the direction is right.

CEF’s Technical Characteristics

CEF itself is a highly mature project. Spotify’s desktop client, parts of Adobe’s Creative Cloud, Epic Games Launcher, OBS Studio’s browser source — all embed Chromium via CEF. Its multi-process architecture mirrors Chrome’s: a browser process manages windows and networking, each page instance runs in its own renderer process, and the GPU process handles compositing and hardware acceleration. This isolation complements Deno’s security model — Deno blocks filesystem/network/environment access by default, and CEF’s sandboxed renderer processes further limit the frontend code’s escape surface.

CEF also supports off-screen rendering (OSR). In normal mode, CEF creates native windows and renders into them; in OSR mode, rendering output goes to an in-memory buffer that the host application decides how to display. This capability is important for Deno Desktop’s winit backend — if Deno wants to support fully custom UI frameworks (like GPU-driven UIs), CEF’s OSR mode can feed web content directly as a texture into the rendering pipeline.

But CEF isn’t free. A single CEF dynamic library (libcef.so) is about 150MB, plus Chromium resource files (.pak, icudtl.dat, locales), totaling around 200MB of disk usage. That’s roughly the same as Electron. Looking purely at binary size, Deno Desktop + CEF isn’t lighter than Electron — that’s not where its advantage lies. The advantage is twofold: first, CEF can be shared, while Electron’s Node.js+Chromium coupling is hard to share; second, Deno lets you drop down to the webview backend for minimal size, an option Electron doesn’t offer.

Three-Way Comparison

Comparing all three approaches in a single table makes the trade-offs clearer.

DimensionElectronTauriDeno Desktop (CEF)
Rendering engineBundled ChromiumSystem WebViewBundled CEF (switchable to system WebView)
Backend languageNode.js (JS)RustDeno (JS/TS)
Binary size150-250 MB3-15 MB200 MB (CEF mode) / 15 MB (webview mode)
macOS compatibilityLatest Chromium, OS-independentBound to system WKWebView versionLatest CEF, OS-independent
Linux compatibilityConsistentDepends on webkit2gtk, performance variesConsistent (own CEF)
Process modelMain + Renderer (Node.js deeply coupled with Chromium)Rust main process + system WebView processDeno HTTP server process + CEF browser/renderer processes
Shared engine potentialLow (severe version fragmentation)Naturally shared (uses system engine)Medium (shared runtime on roadmap)
Frontend framework supportAny JS frameworkAny JS frameworkAny JS framework (including full-stack frameworks like Next.js)
Update mechanismSelf-managedSelf-managedBuilt-in (Deno Deploy-style hot updates)

The most easily misread row in this table is “binary size.” Deno Desktop’s 200MB in CEF mode looks as bad as Electron, but the critical difference is which portion is “app code” and which is “shareable engine.” Electron’s 200MB includes ~180MB of Chromium + Node.js, bundled per-app. Deno Desktop’s 200MB includes ~150MB of CEF, and the shared runtime roadmap means this portion may eventually be stored once. Until the shared runtime ships, Deno Desktop hasn’t beaten Electron on size; once it ships, it has the potential to compress the incremental size of each app to single-digit megabytes — like Tauri achieves today, but without sacrificing rendering engine consistency.

Shared Dependencies: A Linux Wisdom Forgotten by Desktop Apps

Linux distributions have been solving shared dependency problems with package managers for thirty years. libssl.so, libgtk.so, libc.so — there’s always one copy on the system, and every app links to the same one. Version upgrades are coordinated by the package manager, ABI compatibility guaranteed at the distribution level. This system works so well that Linux users have a visceral distaste for “every app bundling its own OpenSSL.”

Why do desktop apps reinvent this wheel? The root cause is a difference in trust models, not technical immaturity. Linux package managers work because there’s a central authority (distribution maintainers) responsible for version consistency across all packages. The desktop app ecosystem has no such central authority — VS Code comes from Microsoft, Discord from Discord Inc., Figma from Figma Inc., with zero coordination between them. Each app developer can only assume “I don’t know what’s on the user’s machine,” so they choose the safest strategy: ship everything they need.

Deno Desktop’s shared CEF runtime plan attempts to find a midpoint between these extremes: not system-level global sharing (which requires OS-level coordination), but managed sharing within the Deno ecosystem. All apps built and distributed via deno desktop share CEF versions managed by Deno’s unified version manager. This resembles Flatpak’s runtime mechanism — multiple Flatpak apps sharing a single KDE/GNOME runtime — but at a finer granularity, sharing only the browser engine.

Whether this path succeeds depends on two variables: first, whether the Deno Desktop ecosystem grows enough apps to make sharing worthwhile (if there are only three Deno Desktop apps, sharing the runtime delivers negligible benefit); second, whether CEF’s ABI stability can sustain “multiple apps depending on the same CEF but updating at different frequencies.” CEF’s API stability is better than raw Chromium’s — its API wrapper layer provides significant buffering — but it’s not monolithic. When CEF has a major version upgrade, how the shared runtime manager handles “app A is compatible with the new version, app B only works with the old version” is a problem for which the Deno team hasn’t yet published a technical solution.

The Bet’s Outcome Hinges On

echelon’s HN comment captured why Deno’s CEF choice is the right direction: Tauri’s system WebView experience on macOS and Linux has been too painful. Tauri’s idea is clean — use native system components, don’t ship duplicate binaries — but reality is that macOS’s WKWebView update cadence is determined by Apple, and Linux’s webkit2gtk quality is guaranteed by a small open-source community. Purity of idea doesn’t compensate for roughness of implementation — users will only remember “this app is unusably slow on Linux,” not “this app uses the system WebView so it saves space.”

Deno’s CEF path sacrifices ideological purity for implementation control. It acknowledges an awkward but true engineering fact: in cross-platform desktop applications, controllability matters more than size. If your rendering engine behaves inconsistently on platforms you can’t control, the disk space you save will be more than consumed by the development time spent on compatibility issues.

But Deno hasn’t given up on size optimization either — the shared runtime roadmap is its fundamental difference from Electron. If shared CEF runtime succeeds, Deno Desktop will simultaneously have “rendering engine consistency” (from bundled CEF) and “small incremental size” (from shared architecture) — something neither Electron (consistency without small size) nor Tauri (small size on Windows, but no consistency on macOS/Linux) has managed to achieve.

Of course, roadmap items cannot be evaluated as delivered products. Deno Desktop is currently still in canary status, its API not yet stable, and the shared runtime is a “future plan.” This field has never lacked beautiful architecture diagrams — what’s missing is a team that can actually engineer a solution to the deceptively difficult problem of shared engine version management. The Deno team has the capability (Deno’s own version management and remote module caching system is relevant infrastructure), but the gap between capability and delivery is the bet itself.