research/comparison.md
Comparison: GPUI Kit, Native Shell, Tauri, Electron, the web UI
Use this file to check D2 against the alternatives on our criteria, and to judge the qmap trade-offs D8 asked for. The first table compares the five ways to put a UI on the endpoint. The second costs the four ways to show qmap from the native app. Numbers carry their metric and source; the vendors measure different things, so compare within a row with care and never across vendors as if equal. Every source was read on 2026-11-09.
The five options
| Criterion | GPUI Kit | Native Shell | Tauri 2 | Electron | Web UI in the browser |
|---|---|---|---|---|---|
| Idle memory | No published hello figure. A gpui-kit 0.8 app peaked at 72.7 MB on Windows in Longbridge's bench (quotes workload) 1. Zed publishes no idle RSS; review sites report 142 to 222 MB idle with no method stated 34. Discord's early GPUI build: about 290 MB with two accounts, one screenshot 35. | macOS ~20 MB footprint / 89 MB resident (TSX); Windows ~15 MB private working set; Linux GTK ~79 MB private / 166 MB resident 2 | Hello world ~389 MB mprof peak on Linux CI (WebKitGTK) 3; ~172 MB for six WKWebView windows on macOS 4 | Hello world ~476 MB mprof peak on Linux CI (2023) 3; ~409 MB for six windows on macOS 4 | One tab: ~76 MB RSS for a static page, ~280 to 380 MB for a web app (Chromium 148, vendor measurement) 5 |
| Idle CPU | Redraws on notify. Upstream GPUI's animations are costly (a spinner: 85 % of a core); GPUI Fast exists to fix that (11 %) 6 | 2 to 5 % on a gallery page at rest (macOS); Direct2D redraws on the main thread, Windows runs hotter 2 | Webview-dependent; unmeasured | Chromium; unmeasured | A tab; a Three.js scene must render on demand or it spins the fans 7 |
| Binary / installer | Hello app unmeasured. Zed 1.27 dmg 88 MB, exe 58 MB, whole editor 8 | 6 to 12 MB zipped hello; 12 to 29 MB executable 2 | 2.8 to 3.0 MB bench binary; 8.6 MB .app scaffold; "less than 600 KB" is the claim [3][4]9 | Runtime zip 121 to 158 MB; "most apps are >100MB" 10 | 0; the agent already ships |
| Tray on 3 OSes | Absent from the kit; a crate is added (see platform.md) | main only, TSX only 11 | tray-icon feature; "Linux: Tray events are not emitted" 12 | Tray on all three; Linux via StatusNotifierItem 13 | n/a |
| Notifications | Absent | main only; macOS needs a signed bundle 11 | Plugin; Windows installed apps only; no action buttons on desktop 14 | Notification; unsigned macOS binaries fail 15 | Web Notifications API |
| Auto-update | Absent; qupdate exists in qcontrol | main only, nsh/updater 11 | Updater plugin, signed manifests; mac, NSIS, MSI, AppImage 16 | autoUpdater mac and Windows; "Linux has no built-in auto-updater" 17 | n/a |
| Deep links | Absent; GPUI has on_open_urls | main only, all three OSes 11 | Plugin; macOS needs an installed bundle; Linux/Windows need single-instance too 18 | setAsDefaultProtocolClient 19 | Browser URL |
| Single instance / autostart | Absent; crates exist | Partial / not found | Plugins, desktop only (D-Bus on Linux) 20 | requestSingleInstanceLock; setLoginItemSettings mac and Windows only 19 | n/a |
| Agent fluency | llms.txt 47 KB, llms-full.txt 4.7 MB, a /skills page; small training corpus; two GPUI version lines to confuse 21 | llms.txt 58 KB, AGENTS.md, skills; three weeks old, absent from training data 22 | llms.txt 2.8 KB; large corpus 23 | Largest corpus; no llms.txt 24 | Vue and Nuxt: huge corpus; our bible and layer |
| Accessibility | AccessKit in GPUI since 2026-05-27, off by default in Zed; the kit claims roles, states and actions tested; no GPUI app known to work with a screen reader 25 | Native assistive tech on mobile; documented gaps on Windows and Linux; AccessKit on the GPUI target 26 | Inherits the webview; tracking issue open since 2019 27 | Chromium's tree, enabled when assistive tech is detected 28 | The browser's |
| Brand reach | Every pixel is ours; themes by semantic tokens | Tailwind 4 or typed styles over native views; no token system; no backdrop filter on Linux or GPUI | Full CSS | Full CSS | Full CSS; the layer as is |
| Time to V1 | Rust; a new framework for us; the pages must be re-expressed | TSX plus Rust; weekly API breaks | Rust shell plus our Vue pages nearly unchanged | Our Vue pages unchanged; the heaviest result | Nothing to build; the thing Tyler wants to stop shipping |
| Languages | Rust | TSX (QuickJS) and Rust | Rust and web | JS/TS | Vue/TS |
Reading the table against the decision: the two web-wrapper rows keep our pages but cost a Chromium or a WebKit process per window, which is the argument that moved the dashboard out of the sensor in the first place. Native Shell's at-rest numbers are small on macOS and Windows but its desktop features are unreleased and its API moves weekly. GPUI Kit has no published footprint for a small app at all; the c131 spike measures that with strobe before anything is promised (R7).
What the measurements do not say
- No idle RSS of a one-window GPUI Kit app exists anywhere public. The 72.7 MB above is a peak under load on Windows.
- Tauri's and Electron's figures are mprof peaks during startup on Linux CI, not idle, and the Electron data point is from 2023.
- The six-window macOS figures come from one blog post, one run.
- Memory metrics differ per OS: footprint, private working set, private dirty, RSS, PSS. Record the metric with every number we publish.
qmap in the native app (D8)
qmap is Vue and Three.js in the web control plane: pawns, rugs and flows in an isometric scene. D8 keeps it a link in V1; Mark asked for the trade-offs laid out so he can judge which matter. Four options, costed from what the web gives.
| Option | Memory | CPU | What the person loses | Build cost | Notes |
|---|---|---|---|---|---|
| 1. Link out to the control plane in the browser | 0 in the app; one tab in the browser (~76 to 380 MB RSS by page type 5) | 0 in the app; the tab's, which depends on whether qmap renders on demand 7 | App context (selection, filters) unless carried in the URL; a single window; offline; an auth hand-off from app session to browser session | Lowest: a URL with state | The conservative V1 (ruled 2026-11-09) |
| 2. Webview embed inside the GPUI window | A WKWebView / WebView2 / WebKitGTK process per view. No clean idle figure exists; proxies: ~29 MB per WKWebView window (six-window total divided, an estimate) 4; nsh's Windows gallery grew ~280 MB after opening WebView2 (estimate) 2 | The scene's, continuous unless on demand; GPU memory for Three.js | Little context (host-to-page messaging exists in wry, not verified for the kit's wrapper); offline unless the build is bundled and served over a custom protocol; the agent's token must be injected | Low to moderate, on an experimental crate | gpui-webview 0.9.1 is in the kit repo, "still a experimental": WKWebView on macOS, WebView2 on Windows, WebKitGTK on Linux X11 only, Wayland unsupported; without gpui-fast the view "cannot be clipped" and covers menus and dialogs; on Windows it covers them regardless 29 |
| 3. Native render in GPUI | The app's own; no second process | 0 at rest (GPUI redraws on notify); pulses and orbit cost frames while animating | Parity with the web qmap: two implementations to keep aligned; Three.js features (camera orbit, pulses) rebuilt | High: a second renderer for pawns, rugs, flows, hit-testing, hover, zoom and pan | GPUI exposes canvas(prepaint, paint) and paint_quad / paint_path / paint_image / paint_svg 30; an isometric 2.5-D scene fits those (no perspective, no lighting). No public API hands a wgpu or Metal texture into GPUI's draw list on all three OSes; only a macOS surface with a CVPixelBuffer is visible 31 |
| 4. Opt-in local web app: the Nuxt build bundled, served locally, opened in the browser on demand | A tab while open; the server is the agent's own Rust HTTP stack (no extra process) or a bundled Node (~20 to 26 MB RSS baseline for Node alone 32; Nitro more, unmeasured) | A tab while open; 0 when closed | A single window; in-app context (same URL-state fix as option 1). Gains offline and a local loopback token | Low: nuxt generate output embedded in the agent plus a loopback route; Tauri documents the same pattern as its localhost plugin 33 | Mark's "optional secondary web app" from the initial prompt. The person opts in, so the cost is theirs to accept |
How to weigh them. Options 1 and 4 keep the app's own footprint at zero and move the cost to a browser the person opened on purpose; 4 adds offline and keeps the control plane out of the loop, for a small build. Option 2 puts a browser engine inside the app's process tree on every launch where qmap is open, on a crate its authors call experimental, with Linux Wayland out. Option 3 is the only one that is truly native and truly cheap at rest, and the only one that costs a second implementation. If the native app ever needs a topology view of this one machine (which harnesses talk to which MCP servers), option 3 at small scale is the honest candidate; for the org-wide view, 1 or 4. The spike can measure 2 cheaply if Mark wants a number rather than a proxy; it cannot measure usage, which is why D8 stayed conservative.
Open questions
- The idle RSS, CPU and binary size of a one-window GPUI Kit hello app on the three OSes. Only the spike answers this.
- The idle memory of an empty WKWebView / WebView2 / WebKitGTK inside a GPUI window, with the metric stated.
- Whether
gpui-webviewexposes host-to-page messaging and whether WebGL content works inside it on each OS. - Whether qmap renders on demand today or runs a continuous loop; this decides the tab's CPU in options 1, 2 and 4.
- Whether any GPUI app is usable with VoiceOver or NVDA; this is the accessibility row's real answer.
Sources
- https://github.com/longbridge/nsh-perf-bench — Windows 11, gpui-kit 0.8.0 on gpui-fast 0.1.10, PresentMon, median of 3, 2026-10-22; "simplified UI, so it isn't a like-for-like UI comparison"
- https://nsh.dev/docs/performance-report.md — nsh's own release-build figures; metric named per OS
- https://tauri-apps.github.io/benchmark_results/ (
tauri-recent-linux.json,electron-recent.json) — hyperfine and mprof on GitHub Actionsubuntu-latest; method in https://raw.githubusercontent.com/tauri-apps/tauri-docs/v1/docs/references/benchmarks.md - https://www.gethopp.app/blog/tauri-vs-electron — macOS, six windows, one run, 2025-04-09
- https://www.superchargebrowser.com/library/chrome-ram-usage-per-tab-2026.md — Chromium 148, Linux
smaps_rollup, clean profile; a tab-suspender vendor, low trust but the only stated metric - https://raw.githubusercontent.com/longbridge/gpui-fast/main/README.md — Linux, release, 144 Hz, mean of two runs
- https://r3f.docs.pmnd.rs/advanced/scaling-performance — on-demand rendering guidance
- https://api.github.com/repos/zed-industries/zed/releases/latest — Zed 1.27.0, 2026-11-04
- https://tauri.app/ and https://v2.tauri.app/start/ — the "600 KB" claim; https://v2.tauri.app/concept/size/ gives no number
- https://www.electronjs.org/docs/latest/why-electron; https://api.github.com/repos/electron/electron/releases/latest — Electron 45.2.3, 2026-11-04
- https://nsh.dev/versions/main/modules/nsh-desktop.md; https://nsh.dev/versions/main/docs/app-updates; https://raw.githubusercontent.com/longbridge/nsh/main/website/modules/nsh.md — unreleased
main - https://v2.tauri.app/learn/system-tray/
- https://www.electronjs.org/docs/latest/api/tray
- https://v2.tauri.app/plugin/notification/
- https://www.electronjs.org/docs/latest/api/notification
- https://v2.tauri.app/plugin/updater/
- https://www.electronjs.org/docs/latest/api/auto-updater
- https://v2.tauri.app/plugin/deep-linking/
- https://www.electronjs.org/docs/latest/api/app
- https://v2.tauri.app/plugin/single-instance/; https://v2.tauri.app/plugin/autostart/
- https://gpui-kit.com/llms.txt (47,730 B); https://gpui-kit.com/llms-full.txt (4,737,688 B); gpui-kit pins
gpui-pre 0.3.12while docs.rsgpuiis 0.2.2 - https://nsh.dev/llms.txt (58,776 B); https://github.com/longbridge/nsh
- https://v2.tauri.app/llms.txt (2,798 B)
- https://www.electronjs.org/llms.txt returns 404
- https://github.com/zed-industries/zed/pull/56065 (merged 2026-05-27); https://github.com/zed-industries/zed/issues/41138 ("Zed doesn't support screen readers", maintainer, 2025-10-28); https://gpui-kit.com
- https://nsh.dev/docs/accessibility.md; https://nsh.dev/docs/gpui.md
- https://github.com/tauri-apps/tauri/issues/207
- https://www.electronjs.org/docs/latest/tutorial/accessibility
- https://raw.githubusercontent.com/longbridge/gpui-kit/main/crates/webview/README.md; https://docs.rs/crate/gpui-webview/latest —
lb-wry 0.53.3, Longbridge's wry fork - https://docs.rs/gpui/latest/gpui/fn.canvas.html; https://docs.rs/gpui/latest/gpui/struct.Window.html
- https://docs.rs/gpui/latest/gpui/fn.surface.html; https://raw.githubusercontent.com/zed-industries/zed/main/crates/gpui/Cargo.toml
- https://cirosantilli.com/javascript-memory-usage-benchmark — Node 16, idle loop and hello script, not an HTTP server
- https://raw.githubusercontent.com/tauri-apps/plugins-workspace/v2/README.md — the
localhostplugin - https://www.devtoolreviews.com/reviews/zed-editor-review-2026 (180 MB idle, dated 2026-02-27, updated 2026-10-21); https://technewsdaily.com/news/how-to-get-started-with-zeds-2026-multiplayer-code-editor/ (222 MB) — secondary, see claims.md
- https://www.heise.de/en/news/Discord-is-rebuilding-in-Rust-leaving-React-and-Electron-behind-11516113.html; https://dev.to/jamilxt/electron-vs-gpui-what-discords-290-mb-rust-rewrite-can-and-cannot-prove-34b6 — the 2026-11-04 announcement, see claims.md