A download manager might run for hours. I only need its interface for a minute: add the file, check where it will go, and leave it running.
Written from my notes and experience building ProtoPeek, with AI assistance for drafting and editing. Adapted from my illustrated article.
BUI: a name for something we already do
I’m calling it Browser User Interface — BUI. You can read it as Borrowed UI, too: your app brings the functionality and borrows the browser.
A local program serves a web interface and exposes an API. The browser handles layout and interaction; the program handles the work. This is a proposed label for a familiar architecture, not a new standard or an invention claim. It is still a GUI.
A CLI can be another entrance to the same core. You still have to implement the commands and decide whether they share a running session.
A browser face. A local backbone.
Say you’re building a label-printing tool. A webpage can open the browser’s print dialog, but it cannot simply call an arbitrary printer driver or vendor SDK. A local backend can use native interfaces such as the Windows Print Spooler API, then expose a specific “print this label” operation to the page.
That is the beauty of the local bridge: web controls without restricting the whole app to browser APIs. The page stays sandboxed; the native program owns the capability. You still handle installation, OS permissions, drivers, and platform differences. Keep the bridge narrow and authenticated, not a general-purpose shell.
The web tooling is already here
As a UI developer, I already have layout, components, typography, devtools, and a familiar path from design to working controls. AI helps me write code; it does not make cross-platform native UI and packaging decisions disappear.
Sometimes a consistent custom interface matters more than native widgets. Electron, used by Discord and Slack, is a reasonable answer: bring the engine along for a predictable rendering target. BUI asks whether this particular tool can use the browser already on the machine. We are often there anyway.
The strongest examples are not mine
- Open WebUI is a substantial self-hosted interface for AI conversations, tools, and knowledge. It can connect to local models or cloud providers. Local UI hosting does not guarantee local computation.
- OpenCode Web starts a local server and opens the browser. Its terminal UI can attach to that server and share sessions. This is more than a settings panel.
- Syncthing has a browser management interface, bound to localhost by default. Its controls can be occasional even while synchronization runs all day.
- grpcui launches a local gRPC inspector operated in the browser. ProtoPeek originated from this project.
These projects illustrate the architecture; they do not use or endorse my proposed BUI label.
What building ProtoPeek taught me
ProtoPeek is my implementation: a local Go workbench with an embedded web interface. The page presents a gRPC request while the Go backend makes the native protocol connection.
As a UI person, I like having room for searchable lists, request forms, and readable results without making desktop packaging the whole project.
Real v0.5.0 release-source capture with fixture transfers, not the latest development shell. The external aria2c engine owns the downloads; the page controls them.
The pp download command handles one URL in a separate engine session; it does not attach to the browser queue. “CLI and GUI” is not a promise of feature parity.
Let the backend carry the work
Choose files, arrange a few steps, start an encoding job, close the tab. Come back later for progress, logs, and results. That is the experience I want from a small workflow tool. The backend owns the job; the page is a window into it. Reconnection, cancellation, and durable job state still need deliberate implementation.
This works for sync, batch conversion, and automation too. Keep heavy work out of the page’s main thread. Web Workers can do that inside the browser; a local backend is useful when a job needs native libraries, OS access, or a lifetime independent of the tab. It still uses the same machine’s CPU and memory.
That layer can be C# for deep Windows integration, Go for straightforward iteration, Rust when its control and safety trade-offs fit, or C++ when the engine is already there. No minimum age for C++—just an existing relationship with your debugger.
I’m using this approach for a client project too, and the UI side has been a breeze. Familiar controls, a separate execution layer, and a useful boundary between them.
What you gain — and what you still own
Possible advantages:
- No bundled browser engine to distribute.
- Familiar web UI tooling and accessibility primitives.
- A core that can work without its interface open.
- Room for scriptable commands and visual controls.
Responsibilities that remain:
- Installation, updates, launch, reconnect, and shutdown.
- A secure local API: loopback binding, Host/Origin validation, appropriate authentication, and CSRF protection. CORS alone is not authorization.
- Browser compatibility, memory, and working data.
- Less control over windows, shortcuts, and OS integration.
ProtoPeek is still evolving. A stripped Windows amd64 build of commit b6037bf on September 6, 2026 was 34.1 MB, including 1.24 MB of web assets. Three idle samples over six seconds, with no connected browser or requested jobs, showed 22.2 MB working set and 53.3 MB private committed memory. Those are different Windows measurements, not numbers to add together. Decimal MB; excludes the browser and external engines. Build flags and scope are in the measurement note.
A CLI command has no continuing process memory after it exits. A backend left running still does. There is no fixed “one tab” cost: measure the backend and incremental browser memory. This short idle snapshot is not a startup, peak-memory, or cross-framework benchmark.
A PWA can add an app icon and standalone window where supported. Installing one does not install or start an arbitrary native backend; you still need a launcher and reconnect behavior.
Sometimes another approach fits better
I love native apps. When platform behavior is central, I want to use the platform—not bundle a browser engine with a tiny utility by reflex. What wears me down is getting that experience right across operating systems. Flutter is a strong custom-UI option, but I still want the platform’s own behavior in some places. That is a preference, not a verdict on what those frameworks can build.
A system WebView is a useful middle ground: web tooling inside an application window. Tauri, for example, uses WebView2 on Windows, WKWebView on macOS, and WebKitGTK on Linux. You gain a dedicated window and closer integration while still testing across engines.
Native controls can be better when platform behavior is central. A bundled engine can be worth its size when consistency matters. This is not a ranking from bad to good.
For a download queue, backup manager, batch converter, or device configuration tool, the controls may need attention for only a few minutes. The process can keep working overnight.
For an editor, design tool, or audio workstation used all day, input behavior, accessibility, shortcuts, and sustained responsiveness deserve much more weight. I tend to prefer native implementations there, but VS Code and OpenCode are good reasons not to turn that preference into a rule.
A little notepad could fit too, if file handling, offline use, and shortcuts work well. I would not choose this by default for my full photo editor: precise input, color management, and large documents deserve their own evaluation. Web technology can build serious creative tools; that does not settle the delivery choice.
The UI tooling was already here. Let the local app do the work.
The full article has an interactive diagram, an Open WebUI example, and the sources. My earlier desktop stack essay covers the wider trade-offs.


Top comments (0)