Electron, Meet the Door: Rust GPUI Boot Times We Didn’t Believe
I used to apologize for first paint. We’d click the icon, watch a window outline, and wait while a miniature web stack woke up inside a desktop shell. Everyone called it “normal.” Then we tried a Rust-based GPU UI stack and the apology died on day one.
Boot felt instant. Scroll never tore. The window behaved like it belonged to the machine, not a tab pretending to be one. After that, “fast enough” didn’t feel professional anymore.
The moment the default flipped
Electron’s been the right trade for a decade: ship once, reach everywhere, move with web talent. But a browser-in-a-window carries weight by design — layout engine, JS runtime, IPC bridges. On modern hardware with 120–240 Hz displays, users notice that weight.
Rust GPUI starts in a different place: draw straight to the GPU, keep state in plain Rust, talk to the OS directly, no browser runtime humming backstage. The result shows up in the first half-second of user experience.
A tiny sanity check (our rough, device-averaged sample): | Scenario | Cold Start (ms) | Idle RAM (MB) | Notes | | ---------------------------- | --------------: | ------------: | ------------------------------ | | Electron shell, one view | 1200–1800 | 250–400 | Warm caches help, still chunky | | Rust GPUI, same surface area | **220–450** | **65–110** | Fewer layers on hot path | (Numbers are representative from a 2025 MBP + mid-range Windows laptop. Your stack and features will move these lines; the shape tends to hold.)
Why the stack feels different (physics, not faith)
- GPU-first pipeline. Views render through a modern graphics path; you’re not paying a DOM/CSS tax to draw panel chrome.
- Single language + zero GC. End-to-end Rust reduces scheduler surprises and GC cliffs; latency is more predictable.
- Smaller surface area. Fewer layers → fewer cold-start initializations → fewer places for input lag to creep in.
- Deterministic lifetimes. Ownership/lifetimes keep UI/data lifecycles tidy without a runtime scanning your heap.
A quick, practical slice (illustrative Rust GPUI) This is a simplified, idiomatic Rust sketch to show the “shape” of building a view in a GPU-native stack: one binary, explicit state, event → render without a browser in the middle. // cargo.toml (conceptual) // [dependencies] // gpui = "0.1" // your GPU UI crate // anyhow = "1" // time = "0.3"
use anyhow::Result; use gpui::{App, Color, Event, Font, Rect, View, Window};
struct Counter {
count: i32,
}
impl View for Counter {
fn event(&mut self, e: Event) {
match e {
Event::Click(_) => self.count += 1,
Event::Key("Escape") => self.count = 0,
_ => {}
}
}
fn draw(&self, w: &mut Window) {
w.clear(Color::rgb(18, 18, 20));
w.text(
format!("Booted in a blink. Count: {}", self.count),
Font::system(18.0),
Rect::px(24, 24, 480, 60),
Color::rgb(230, 230, 235),
);
w.button("Click me", Rect::px(24, 96, 160, 40));
}
}
fn main() -> Result<()> {
App::new()?.window("Hello GPUI", Counter { count: 0 })?.run()
}
What to notice: no DOM, no CSS, no IPC bridge; input becomes events, state mutates, the GPU draws the frame. Cold start = initialize app + font + surface, then render.
The pipeline, side-by-side (why boot feels instant)
Electron path ───────────── User click
↓
OS shell → Chromium (DOM, CSS, JS, layout)
↓
IPC to app logic (Node)
↓
GPU compositor → window
Rust GPUI path ────────────── User click
↓
OS shell → Rust view/state
↓
GPU renderer → window
Each extra layer adds init time, memory, and places for latency to hide. Remove layers; remove apology. Where Electron still makes sense (and you shouldn’t be heroic)
- You need a live browser surface (authenticated web apps as first-class screens).
- Your team’s speed is web-native and the codebase is mature; a rewrite slows value for months.
- Plugin ecosystems your product depends on are web-only today.
This isn’t about dogma; it’s about picking the physics that fits your product. A humane migration strategy (no flag day, real wins)
- Pick one surface that users touch every session (Command Palette, Settings, Quick-Add).
- Clone the UI in Rust GPUI as a small companion app or embedded surface.
- Measure two things only: time-to-first-interaction and p95 input latency under load.
- Ship to a ring (internal, then 5% public) and collect “felt fast?” feedback.
- Win the next surface if the first one earns it. Repeat.
Success is a proof that changes minds, not a rewrite announcement. Guardrails & trade-offs (what you must plan for)
- Accessibility: without native widgets, you own focus order, semantics, and screen reader hooks. Budget time.
- Text shaping & internationalization: make sure your stack handles complex scripts and fallback fonts.
- GPU/driver quirks: test on integrated + discrete GPUs; add software fallbacks for ancient drivers.
- Packaging & updates: bundle fonts/assets sanely; pick a delta-update story early.
- Team skills: Rust UI feels different from styling a page; plan a learning curve (pairing helps).
The 7-day boot-time challenge
- Day 1: instrument current cold start (time to first interactive paint). Write it down.
- Day 2–3: build a GPUI “Hello Settings” that reads/writes one real preference.
- Day 4: add a realistic list view (e.g., recent files) and a keyboard shortcut.
- Day 5: package for macOS + Windows; measure cold start again on clean boot.
- Day 6: dogfood with the team; collect “felt laggy?” notes.
- Day 7: decide: ship to 5% as a toggle, or cut and keep Electron for now. No sunk-cost guilt.
If the numbers don’t move, stop. If they do, you just purchased user trust. Do this today
- Time your current cold start with a simple stopwatch + logging hook.
- Identify one UI that doesn’t need a live browser.
- Sketch the event → render flow on a whiteboard. If it’s simple, it wants GPUI.