Only for language models

Go(lang) GUI libraries, part deux

By Sean E. Russell on on Permalink.

Since I last wrote about GUI libraries, some new libraries have come to my attention and I’ve had some time daily driving the various toolkits.

The User Experience

Nucular, while easy to use and having impressive specs, has a couple of rough edges which incline me to keep looking for alternatives.

  • Capitalization on some platforms is wonky. I can not, for the life of me, get a text entry field to allow capital characters on mobile. The same code works fine on desktop (X11/amd64/physical keyboard), but on my FLX1s (Wayland/arm64/virtual keyboard) only lower-case characters are recorded.
  • Nucular ignores the StartupWMClass attribute, which tells a program to not launch duplicates of itself. This makes it behave poorly with the common mobile pattern of switching apps by “relaunching” it with the desktop icon, which is almost the only way to jump between apps on Phosh since Phosh doesn’t order applications by MRU.If you launch program A, then B, C, D, E, and F; and then you need to jump back and forth between A and F, on Phosh you have to repeatedly scroll between the ends of the running program windows to context switch. This is among the most aggregious UX “designs” I’ve ever encountered; the work-around is to jump between apps by re-launching them. While it’s not Nucular’s fault Phosh behaves this way, ignoring the StarupWMClass attribute is a Nucular bug This means that relaunching a Nucular program always creates a new instance, except that only one instance – the last – ever receives input events. It’s broken, no matter how you look at it.
  • Getting scrolling right is fussy. Sizing widgets in a container is hard. Acxetumi has several weird bugs because I haven’t jumped through the hoops of trying to figure out how Nucular decides how much space a container allows; the same logic produces two different widths for the first and second elements in a vertical list, for example. Scrolling is jumpy and the last element in a scroll list is often cut off and inaccessible, as you can’t scroll down far enough to get to it. While I have no doubt I could eventually work around these issues, it substantially complicates the code and pushes the “complexity” metric closer to gioui.
  • It’s less pretty than I originally thought, in practice. The themes are definitely muted: instead of clear, distinct colors, most are shades of grey – the white theme is off-white, darker off-white, and off-black. The low contrast makes visibility on a mobile device poor. This could easily be fixed by adding another theme, again increasing the complexity of any program.

New alternitaves

The (to me) new GUI toolkits are:

Both are GPU-accelerated. go-gui appears to be more fleshed-out, with a larger toolbox of widgets and more active development. Both compile without CGO and produce self-contained binaries (not linking to more than a minimum number of libs).

Acxetumi’s UI is pretty simple and it makes a good trial application for libraries. It’s simple enough to make it not a major undertaking to redo the GUI, but includes a list-of-compound-objects Acxetumi is a shopping list, and the list items are a checkbox and an editable text field for the shopping item. Many toolkits include a toggle + label; it’s the “editable” part that steps outside the box. which I’ve found stumps some GUI toolkits. It needs to allow the user to select the shopping list file, and so needs a file selector; it needs a way of showing alternative content such as “About” and “Help” information. It’s simple, but has good coverage of edge features; every library can draw buttons and labels – it’s the edge cases where things fall down.

Furthermore, my target for GUI apps is my mobile phone, which is a Linux phone running Phosh. The library should cross-compile to arm64 from amd64, and it should run on a Linux phone. Both seem like reasonable expectations, but I’ve encountered issues with each of these. gioui requires compiling under a special arm64 docker container to get arm64 binaries; go-gui struggles with scaling and Wayland GL which FuriOS FuriOS is the Debian-based OS that FuriLabs provides for the FLX1s phone. provides. It would be really nice if binaries could be compiled for Android and iOS; gioui has and Android target.

gogpu

gogpu’s module layout is not very intuitive. Widgets are scattered around in submodules, making them hard to find and remember. You add a widget.Widget to the root, which can be a primitives.BoxWidget or a core.Button or a widget.Canvas or any one of a <widget-subpackage>.Widget… it’s organized on obtuse boundaries. I had a couple of problems and concerns with gogpu:

  • The binaries are large, about double the size of gioui or nucular versions of the application. The nucular-based version of acxetumi is ca. 5,700k. An incomplete I didn’t finish wiring it up; it doesn’t save changes, and it’s missing the file selection feature stripped gogpu-based binary is 12,730k.

  • Swapping to another virtual desktop and back left the acxetumi window with a crop of the other desktop, until I mouse over the window and cause it to redraw; I suspect this is related to the GPU renderer and might be worked around.

  • Applications do not build against the most recent tags. The examples are pinned at earlier versions, and if those versions are used programs can be compiled; however, updating to the most recent tags causes the examples to fail as well. This does not inspire confidence.

  • There are several indirect dependencies required to use the library, all by the same developer, and every library is separately tagged. This makes version mananagement quite tedious. For example:

      github.com/gogpu/gg v0.50.3
      github.com/gogpu/gogpu v0.44.1
      github.com/gogpu/ui v0.1.37
      github.com/gogpu/gpucontext v0.21.0
      github.com/gogpu/gputypes v0.5.1
      github.com/gogpu/naga v0.17.15
      github.com/gogpu/wgpu v0.30.10
    

    There may be a reason why things are broken out this way; perhaps gputypes is used by several other projects.

  • CPU rendering did not lay out properly

  • Widgets did not reliably react to events.

I spent about a day messing around with gogpu. The API has a distinct character, very reminiscent of Java APIs, and once you get the feel for it it’s quite comfortable even if it doesn’t feel idiomatically “Go”. It has a lot of potential, but I had enough trouble working with it I didn’t finish the implementation or try running the result on the phone.

This trend of not needing CGO is nice. The compiled binary linked to:

linux-vdso
libresolv
libc
libdl
ld-linux-x86-64

Except for libdl, that’s about par for the course for simple command-line Go tools.

go-gui

acxetumi using go-gui acxetumi using go-gui go-gui is fairly straightforward: the API is simple and easy to use, with minimum boilerplate. Most importantly, it compiles and runs without issue, and the UI behaves as you’d expect it, with very little tweaking necessary to get good looking results. It may just be me and 30 years of developed expectations, but doing things such as vertically centering items in a row are unsurprising: it’s a VAlign: gui.VAlignMiddle attribute.

Sadly, the size trend is even worse than for gogpu: the unfinished (same state as gogpu) binary (stripped) is a whopping 22,510k. 22MB is enormous.

The compiled binary linked to:

linux-vdso
libresolv
libc
ld-linux-x86-64

Nice.

Why are the binaries so big?

Probably because of this:

github.com/alecthomas/chroma/v2 v2.27.0 // indirect
github.com/dlclark/regexp2/v2 v2.2.2 // indirect
github.com/ebitengine/purego v0.11.1 // indirect
github.com/go-gui-org/go-glyph v1.26.1 // indirect
github.com/go-pdf/fpdf v0.9.0 // indirect
github.com/go-text/typesetting v0.3.5 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/jezek/xgb v1.3.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/tdewolff/parse/v2 v2.8.16 // indirect
github.com/yuin/goldmark v1.8.5 // indirect
github.com/yuin/goldmark-emoji v1.0.6 // indirect
golang.org/x/image v0.46.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect

go-gui pulls in a lot of stuff. To be fair, although I’m not using markdown in my project yet, I can see using it in an About or Help text dialog. go-gui is higher-level than gioui, but not much higher level than nucular, so the fact that go-gui binaries are four times the size of nucular binaries is disappointing. It’d be less of a concern for applications targetting the desktop, but it’s a real issue for mobile devices which are more memory-constrained – a program is going to use at least as much memory as the binary itself, and a cavelier attitude toward binary sizes implies the same attitude toward memory use in general.

Conclusion

BLUF, My current ranking is now:

  • go-gui
  • gioui
  • nucular
  • go-shirei

One disappointing aspect of go-gui is the use of Claude Code. Despite this, and the enormous binary sizes, I chose to finish the go-gui implementation and am quite happy with the result. It’s a nice API to use: simple, intuitive, and without extraneous tweaks which e.g. nucular requires to get good-looking results; I haven’t yet found something it can’t do, or which is hard to achieve. It does look better than the other libaries, except for gioui; IMHO, gioui is really bare-bones, but I like it – most people will probably prefer the look of go-gui. go-gui also supports themes, and contains several convenience dialogs and provides access to the native platform’s file chooser – a smart decision. It was easy to build a list of compound widgets without a lot of hubbub. The API doesn’t stray outside of common Go idioms, and while I have yet to evaluate memory and CPU use and I haven’t tried cross-compiling for Android, it’s hard to go-gui.

Postscript

Well, hell. After getting everything nice and tidy, and even adding some polish, it turns out go-gui programs won’t run on the FLX1s, which is what I’m trying to develop GUI apps for. There’s some issue with EGL, almost certainly yet another Wayland SNAFU. So I guess I’m back to gioui sigh.

XGB: conn.go:69: Could not get authority info: open /home/furios/.Xauthority: no such file or directory
XGB: conn.go:70: Trying connection without authority info...
gl: gl: init EGL display: eglChooseConfig: no matching config (egl error 0x3000) — no GPU context (update the GPU driver, leave RDP/VM, or install Mesa on Linux)

Postscript II

@mike-ward, the owner of the go-gui repos, responded to my tickets within minutes and suggested a work-around; this was followed by a commit which implemented the work-around. There are two remaining issues which I’m going to have to poke at; first, under Wayland go-gui applications are rendered at 2x the size of other programs. Second, some facilities are not supported under Wayland, such as the native file dialog. I suspect I can work around both, and given how active @mike-ward is in developing the project it’s possible fixes will not be long coming. Expect a Postscript III within days – acxetumi is an application I use frequently, so keeping it working is high on my priority list.

Postscript III

Every day I’ve posted a ticket, and every day @mike-ward has responded with a fix on main. He figured out the scaling issue and implemented a work-around, and I found an issue resulting in high CPU use, and he committed a fix for that as well. I won’t hesitate to reach for go-gui for future GUI programs for my phone.