Back to Devoured - October 26, 2026
DEVOURED
A technical deep dive into Cap's migration to GPUI

A technical deep dive into Cap's migration to GPUI

Tech Cap
Cap 0.6.1 proves that replacing web-view based UI (Tauri) with GPU-rendered Rust (GPUI) results in massive performance gains and cross-platform consistency.
What: Richie McIlroy explains Cap's migration from Tauri/WebViews to GPUI. By drawing the interface directly on the GPU using shaders and Metal/Vulkan/Direct3D, they achieved 77% lower CPU usage and 88% less memory consumption.
Why it matters: This transition signals a growing trend where desktop applications abandon the browser-as-a-runtime paradigm in favor of native game-like rendering engines for better performance and deterministic behavior.
Takeaway: If your cross-platform tool is experiencing erratic UI bugs, consider moving from WebViews to a GPU-based framework like GPUI, which uses consistent native code for drawing across platforms.
Deep dive
  • The old architecture relied on web views (WebView2/WKWebView) that differed across operating systems, causing bugs.
  • GPUI uses a game-like architecture: describe the screen, draw on the GPU.
  • Frame path: Old app sent frames over WebSockets; new app uses shared memory (IOSurface on Mac) to keep frames on the GPU.
  • 65 contributors merged 111 PRs during the transition, emphasizing community-driven performance optimization.
  • Porting cost: Roughly 182,000 lines of Rust replacing a similar amount of TS/Rust, but losing CSS/Vite hot-reload.
Decoder
  • GPUI: A Rust-based UI framework developed by the Zed team that renders user interfaces using the GPU.
  • Tauri: An application framework that builds small, fast, secure desktop applications using existing OS-native web view engines.
  • WebKitGTK: The port of the WebKit browser engine used by the GTK toolkit, common on Linux desktop applications.
Original article

Cap 0.6.1 is out. It's the biggest change we've made to the desktop app since we started, and it's the first version that doesn't have a web browser inside it.

Until now, Cap's desktop app was built with Tauri. Tauri is a lot like Electron, in that you build your app's interface with HTML, CSS and JavaScript. The difference is that Electron ships a full copy of Chromium inside every app, while Tauri uses the browser engine that already comes with your operating system, which is called a web view. So every window in Cap was really a small web page, running on Safari's engine on a Mac, Microsoft Edge's on Windows and WebKitGTK on Linux.

That kept Cap small and fast for a long time. It also meant our interface was running in three different browsers that we didn't control, and a lot of our bugs came from there. Cap's panels overflowed on Windows if you'd changed your text size, windows showed up blank on some Linux graphics drivers, and area selection on WebKit cancelled itself as soon as you started dragging.

In 0.6.1 every window is drawn on your GPU by Rust code instead, using GPUI, the UI framework the Zed team built for their code editor. It should look and work like the Cap you're used to.

We had the first GPUI build working overnight in August. We could have shipped it sooner, but we wanted to roll it out safely, so it spent a few weeks as an experimental option in 0.6.0 first. The engine mostly came across as it was, with a few changes to get the most out of the new app.

Drawn like a game

If I had to explain it in one line, Cap's interface used to work like a web page, and now it works more like a video game.

A web page is a document. Each Cap window was a tree of HTML elements, and the browser had to style, lay out, paint and composite all of it, using whatever browser engine your OS shipped with.

Games work differently. They describe the whole screen and draw it on the GPU each time, and GPUI does the same. When a window changes, GPUI lays it out again and turns it into one flat list of shapes. Cap's main window only uses five kinds of shape.

  • Shadows. Blurred rounded rectangles under the window and its cards.
  • Quads. Every box, with its fill, border and rounded corners.
  • Glyphs. The text. Each letter is cut out of a texture that stays on the GPU.
  • Icons. SVGs, rasterized once and reused on every frame.
  • Images. Full-colour pictures, like the Cap logo.

That list is sent to the GPU in batches and drawn by a handful of small shaders, using Metal on macOS, Direct3D 11 on Windows and Vulkan on Linux. There's no DOM, CSS engine or JavaScript involved. One thing Cap doesn't copy from games is redrawing 60 times a second for no reason. If a window has nothing new to show, it stops asking for frames.

Where we started

Cap has always been made of two parts.

The first is the engine. It captures your screen, camera, mic and system audio, writes everything to disk without dropping frames, renders your edits on the GPU and encodes your exports. It's been written in Rust from day one, and it lives in a set of crates (Rust's name for libraries) that don't know anything about windows or buttons.

The second is the interface. Up to 0.6.0 that was a SolidJS app running inside Tauri. Each Cap window (the main window, the camera bubble, the recording bar, the editor and so on) had its own web view, which is basically a browser tab without the browser around it. The JavaScript running in those web views couldn't touch the recording engine directly, so every click turned into a message to the Rust side, which did the work and sent the result back.

Why the rewrite

Three operating systems, three browsers

Using the system web view means you're really using three different browsers. The same CSS and JavaScript went through three different layout engines and three different text renderers, and each of them had its own quirks.

  • Blank windows on Linux. On some graphics drivers, WebKitGTK's DMA-BUF renderer leaves web view windows completely blank.
  • Text size on Windows. WebView2 picks up its zoom level from the Windows "Text size" setting, so anyone who had turned that up saw Cap's panels overflow.
  • Moving between monitors on Windows. If you dragged a window onto a monitor with different scaling, WebView2 didn't always pick up the new scale.
  • Area selection on WebKit. When you clicked and dragged to select an area, WebKit could fire a blur event before it had finished capturing the pointer.
  • Lost clicks. The overlay you pick a display or window from was its own web view. In some cases it closed itself before the click on the record button had made it across to the Rust side.
  • Crashes in the macOS picker. Opening and closing the display picker a few times could crash Cap on a Mac.

All of these came from the browser layer, which we couldn't control or update. Getting rid of that whole category of problem is the main reason for this release.

A detour through Electron

In August I tried moving Cap from Tauri to Electron, mostly out of curiosity. Electron ships its own copy of Chromium, so at least we'd have the same browser on every platform. After a night of optimising, the Electron build was faster than the Tauri app we had in production. That still got me wondering how far we could go with a fully native interface.

Picking a stack

Moving off the web view meant we had to draw the interface ourselves. We went with GPUI for four reasons.

It's Rust. So is Cap's engine. In Tauri, the interface and the engine talked to each other over a bridge. GPUI doesn't need that. The editor calls the same engine crates as the CLI, with the same types, in the same process.

Everything is drawn on the GPU. It's the game approach from earlier. There's no browser engine sitting between Cap and the screen, and we can read and change every part of it.

There's one implementation for every platform. Layout, text and drawing all run the same code on Mac, Windows and Linux. The only thing that changes is a thin layer at the bottom.

It still reads like Tailwind. Width, height, padding, flex, rounded corners and backgrounds are all written almost the same way.

How it's built

We rewrote Cap's interface and kept the engine. The old app and the new one share fourteen engine crates, covering screen and camera capture, the recorder, the renderer, the editor's playback engine and the exporter.

Getting video frames to the screen

The biggest single change is in the editor. When you hit play, Cap's renderer puts together your recording, background, cursor, camera, zooms and captions on the GPU, 60 times a second. What's changed is how each finished frame gets onto your screen.

A web page has no access to the GPU memory Rust is using, so in the Tauri app every frame went through six steps involving copying into regular memory and passing to a JavaScript worker. On a Mac, the new app copies each frame into an IOSurface, which is a chunk of graphics memory that macOS lets two renderers share, and GPUI draws directly from that. The frame stays on the GPU the whole way.

Making it feel like the same app

We wanted 0.6.1 to feel like the Cap you already know. So we went through every window and checked it against the old version, one interaction at a time.

  • Text fields. In GPUI we build the text field ourselves on top of the operating system's text input APIs. Ours is about 2,700 lines of Rust.
  • One-line CSS. In GPUI some of them became shaders, some needed a patch to the framework, and for some we changed the design.
  • Windows that aren't normal windows. In the Tauri app the recording bar was a web view. In the new app the bar is a native panel and that logic doesn't exist anymore.
  • Idling properly. GPUI wakes up on every screen refresh to check if anything needs redrawing. We patched it so that if a window has had nothing to draw for 100 ms, it stops checking.

Performance

Most of the CPU saving comes from the new frame path. Most of the memory saving comes from dropping the browser engine. Every Tauri window had its own web view, with its own JavaScript runtime, layout engine and caches, even when it was sitting there doing nothing.

Trade-offs

  • We have to build things the browser used to give us. The new app is about 182,000 lines of Rust, which is almost exactly the size of the 181,000 lines of TypeScript and Rust it replaced.
  • Keyboard navigation isn't everywhere yet. In GPUI we have to set it up window by window, and we haven't finished.
  • No hot reload. Rebuilding the GPUI app only takes a few seconds, but you still relaunch it and click back to whatever you were working on.
  • A smaller ecosystem. GPUI is young and it's built for Zed first.

Where we go from here

Everything we build from now on will be on top of the native app. Next on the list is full keyboard navigation in every window, Cap in more languages, and more performance work, now that we can change every step from capture to screen.

Explore all 69 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi