I got tired of webviews and CGO, so I wrote my own pure-Go GUI library

Hey everyone,

I love Go, but building desktop apps with it has always been a massive headache for me. It usually feels like you have to pick your poison: either wrestle with CGO and ruin Go’s amazing cross-compilation, or bundle an entire web browser just to draw some buttons using HTML and JS.

I know Gio exists—and its rendering engine is fantastic—but the learning curve and raw API always felt a bit too steep when I just wanted to throw a quick tool together.

So, I built Proton.

It is a pure-Go GUI library built on top of Gio, but designed with an immediate-mode style API that is much easier to pick up. I basically wanted something where I could just define a window, write a draw function, and have things stack on the screen without dealing with XML files or weird component lifecycles.

Here is why I think it is pretty neat:

  • Zero CGO: It is 100% Go. You can cross-compile a Windows or macOS app straight from your Linux machine without fighting a C compiler.

  • Simple State: There is no setState or virtual DOM to manage. You just keep your state in standard Go structs.

  • Decent Defaults: I got tired of building apps that look like Windows 95, so I baked in 46 themes (like Nord, Dracula, and Catppuccin) that you can swap with a single line of code.

  • It actually has widgets: It’s not just buttons and text. I’ve added data tables, draggable split panes, search inputs, and modals.

  • API Immunity: The whole thing runs through a proton.Context interface. If Gio’s internals change tomorrow, your app code won’t break.

It is still in early development (v0.2.7), but I’ve been using it for my own tools and it is finally making Go desktop dev fun for me again. The exact same code also runs on Android, which is a fun bonus.

If anyone is interested in pure-Go desktop development, I would really appreciate it if you took a look. The repo is CzaxStudio/proton on GitHub.

Docs: Proton-Docs

I am very open to feedback, roasting of my code, or feature requests!

1 Like

A quick technical look at CzaxStudio/proton — a Gio-based GUI lib that “doesn’t make you want to switch to web dev”

Proton bills itself as a pure-Go GUI library layered over Gio, with two headline claims: no Gio types leak into the public API (“API immunity”), and no CGO required. The design intent is reasonable — a Context interface hides the renderer, and the immediate-mode calls are bridged to Gio’s retained layout pass via a sync.Pool and per-frame result maps.

The problem is that main does not compile.

$ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build .
./alert.go:32: undefined: Win
./extra.go:44: undefined: Win
./layout.go:9: undefined: Win
...

The type Win is referenced by ~52 exported functions across 15 files but is defined nowhere. The history explains it: an earlier type Win struct was refactored into a Context interface plus an unexported winImpl. The refactor reached the text/button widgets, the examples and the README — but not the rest. The result is a split-brain API: text, buttons and the v0.2 widgets take Context, while every layout primitive (Row, Column, Pad, Gap, Grid, the Split family) plus Alert, Toast, Tabs, Accordion, ContextMenu still take the dead *Win. The README’s own “Layout” section cannot compile as written.

A few other findings worth noting:

  • No tests, no CI, no .gitignore. A .github/workflows dir existed and was deleted. A one-line go build ./... gate would have caught the breakage.
  • The “Zero CGO” claim is only half true. Cross-compiling to Windows with CGO_ENABLED=0 genuinely works. Cross-compiling to darwin/arm64 with CGO off fails (undefined: Functions in Gio’s internal/gl) — the macOS backend needs cgo and a macOS toolchain, which is exactly what the pitch says you avoid. Linux needs cgo plus system libs (disclosed lower down).
  • Concurrency. Widget state lives in package-global unsynchronized maps (clickResults, boolResults, …), while Run() launches one goroutine per window. Any multi-window app risks a concurrent-map-write panic. (Single-window is fine; the race detector couldn’t be run since it doesn’t build.)
  • Hygiene. New(name string) ignores its argument; 6 files fail gofmt; 3 have CRLF endings; build.sh has a CRLF shebang and points at a non-existent examples/cybertool; a stray vendor/modules.txt and a 1,185-line marketing index.html sit at the module root.

To be fair: the decoupling idea is sound, the migrated portion of the code is idiomatic, and it’s MIT-licensed. The fix is conceptually simple — finish the Win → Context migration — but it touches ~52 signatures across 15 files, and nothing else matters until the tree builds.

The open question: given where the Go ecosystem is now — templ (type-safe HTML components with real LSP support) plus htmx, and a webview wrapper like Wails when a desktop window is needed — how much appetite is left for a native-Gio abstraction layer whose selling point is avoiding the web stack? templ sits at a different layer (it renders HTML through a browser/webview; Gio renders natively with no browser), so it isn’t a strict replacement for a native GUI. But it arguably already solves the pain Proton claims to address, with far more mature tooling. Is there still a clear niche for a Gio wrapper of this kind, or has the type-safe-templates-plus-htmx approach absorbed most of that demand?

1 Like

Thanks for pointing that out. I will try my best to fix the issues you mentioned. Thanks for trying Proton and in the next version, you will have most issues fixed.

Proton is not replacing anything. It’s just trying to fix the pain, go users often get. It is not a competitor of templ but a helpful library made for go developers.