8 ms·
Show HN: Spot – Simple, cross-platform, reactive desktop GUI toolkit for Go
Hi HN, I’m excited to share Spot, a simple, cross-platform, React-like GUI library for Go. It is just a few days old and has lots of missing features but I'm happy with the results so far, and looking for some design feedback.
Spot is designed to be easy to use and provide a consistent API across different platforms (mainly Mac & Linux). It’s inspired by React, but written in Go, aiming to combine the best of both worlds: the easy tooling & performance of Go with a modern, reactive approach to UI development.
Key features:
- Cross-platform: Leveraging FLTK[1] & Cocoa[2], Spot works on Mac, Linux, and the BSDs with plans for native Windows support in the future.
- Reactive UI: Adopts a React-like model for building UIs, making it intuitive for those familiar with reactive frameworks.
- Traditional, native widget set: Utilizes native widgets where available to provide a more traditional look and feel.
Why I built it:
I was searching for a cross-platform GUI toolkit for Go that had a more traditional appearance, and none of the existing options quite met my needs. I then started playing with Gocoa and go-fltk and suddenly I worked on an experiment to see how challenging it would be to build something like React in Go, and it kinda evolved into Spot. ¯\_(ツ)_/¯
In 2024, is there a still place for classic desktop GUIs—even with a modern spin?
I’d love to hear your thoughts, feedback, and any suggestions for improvement. Also, contributions are very welcome.
Thank you for checking it out!
[1] https://github.com/pwiecz/go-fltk https://github.com/pwiecz/go-fltk
[2] https://github.com/roblillack/gocoa https://github.com/roblillack/gocoa
- ASalazarMX 2y agoI don't know how to take this in. On one side, it's great to have a cross-platform GUI toolkit that's easy to use; on the other side, it feels like a React-like UI is contrary to the spirit of simplicity and resource efficiency of Go. I'm sure it has its perfect use cases, though.
- deleted 2y ago[deleted]
- da_rob 2y agoWhile I agree, that the virtual widget tree has a certain overhead, the typical desktop app you would create with Spot probably does not have 1000s of components—more like ten to twenty probably. Compare that to the immediate mode GUIs that currently are en vogue, that re-render all controls all the time. Spot does not have any optimizations regarding render performance at the moment and it might never be necessary to add them. On the other hand, due to the reactive programming model, I really like how state management get a lot clearer—especially when working with multiple goroutines.
- ygnasygnoimqwb 2y ago> runtime.LockOSThread() > https://github.com/roblillack/spot/blob/main/ui/init_fltk.go#L23 https://github.com/roblillack/spot/blob/main/ui/init_fltk.go... Does this imply GOMAXPROCS needs to be set to at least 2?
- da_rob 2y agoNo, it just means that the main goroutine should not be moved onto another OS thread to ensure that it is always the same OS thread which will run UI updates. This is necessary for most UI libraries and in Spot's case implemented for both the FLTK and the Cocoa backend.
- scosman 2y agoCool! What does building for cross platform look like? Would love if there was a command that produced my MacOs .app and Windows exe without me having to dive into package management/containers/signing headaches per platform.
- da_rob 2y agoThe backends (FLTK/Gocoa) are using CGo, which currently does not support cross-compiling as far as I know. So you still need to have a build pipeline with multiple operating systems, signing/notarization tools, etc. :(
- scosman 2y agoToo bad. I swear half the electron apps use it for ease of Xplatform build. I’d love something like this for giving my CLIs UIs.
- mappu 2y agoYou can cross-compile Cgo, you "just" need a C compiler and linker that works for the target platform. osxcross, xgo have some or maybe you can set CC=zig cc. For macOS you need signing/notarization tools either when building natively or when cross-compiling, it's not any different. `rcodesign` has made this process much easier in recent years.
- apitman 2y ago+1 for `CC=zig cc`. Feels like magic
- felipefar 2y agoThat'd be amazing not only for go apps but for software built on other languages as well. I'd pay for a good, straightforward build packing + crash reporting + autoupdater solution for my apps.
- allanrbo 2y agoWas looking for something like this some years back. Though I wanted Windows support too. Ended up switching to C++ to use wxWidgets, giving me small self contained binaries.
- da_rob 2y agoBig fan of WxWindows, I'm just too invested into Go these days. :) Self-contained Spot "Hello World" is 2.3MiB on my Mac. Not pretty, but works for me.
- mappu 2y agogo-fltk does build and run on Windows, pretty well actually. For a native toolkit, I was impressed to see FLTK supports Ctrl-+ and Ctrl+- to zoom the entire application like a browser. And https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#widget-themes https://github.com/fltk-rs/fltk-theme?tab=readme-ov-file#wid... really improved my impression of how "native" FLTK can be made to look. On a related note, I discovered GoVCL https://z-kit.cc/en/ https://z-kit.cc/en/ recently and am interested to try it out.
- zerr 2y agoThere is wxGo, but sadly the project is not maintained.
- 0xedd 2y ago[flagged]
- jakjak123 2y agoVery cool idea! If someone implements GTK I would absolutely use it, its just fltk is no fun in Linux world
- Heliodex 2y agoIf only I had known about this (or judging by commit history, if it existed) ~3 weeks ago. I've been saying for ages that either React ported to Go or a React-like framework for Go would be an incredible development experience, so this looks perfect (I used to be a big hater of React.js until I was enlightened by React.lua).
- apitman 2y agoDon't leave us hanging! What did you use instead 3 weeks ago?
- Heliodex 2y agoWails <https://wails.io https://wails.io>. I wanted to use Go but wasn't as familiar with native UI as with HTML/CSS/JS, I tried out most of the other popular Go UI frameworks too though they didn't feel as comfortable to use as Spot's React-like model. Wails is still pretty epic as well though.
- parlortricks 2y agooh this looks great
- rkwz 2y agoI had the same problem - really liked the way I used to do component composition in React, it was hard to go back. Eventually found a way to do something similar using just the standard Go html/template. I’ve written about the implementation here: https://www.sheshbabu.com/posts/react-like-composition-using-go-html-template/ https://www.sheshbabu.com/posts/react-like-composition-using...
- biomcgary 2y agoThis a great write-up and similar to an approach that I stumbled upon a few years ago. Have you thought about HTMXing this approach to get away from full page renders?
- heywire 2y agoI’ll have to give this a look! I’ve been looking for a simple way to use Go to write an internal development tool which is basically just a form with some buttons and text fields. I tried Gio, but had a hard time with wrapping my head around it. Right now I’m using wails and like it much better. This looks interesting and worth a look!
- da_rob 2y agoPerfect use-case, IMHO! Looking forward to hear your thoughts ocne you tried it.
- getcrunk 2y agoFltk supports windows. Will you be using another solution and that’s why u don’t support windows yet?
- da_rob 2y agoGiven the right C compiler and everything, Spot should work without changes on Windows (and select the FLTK) backend. My (low prio, though) goal is to implement a Win32-based backend, though, the first step is done: https://github.com/roblillack/spot/pull/4 https://github.com/roblillack/spot/pull/4
- KingOfCoders 2y agoI applaud your effort, but cross plattform with no Windows support?
- jppittma 2y ago[flagged]
- KingOfCoders 2y agoThanks, I'd wish I could get my G4 Cube back.
- poisonborz 2y agoWhy is platform shaming tolerated around here
- IshKebab 2y agoOh you mean one that can sleep reliably and has battery life of more than 2 hours?
- KingOfCoders 2y agoMy desktop has no battery.
- IshKebab 2y agoGood for you. My laptop does.
- KingOfCoders 2y agoYes, if you need a laptop for work and can't live without one, there is no other way but to live with the downsides of high costs, small screen and a battery. Lucky me I no longer have to use a laptop for work and have a desktop without all the downsides.
- 2y ago
- felipefar 2y agoI'm curious to hear your take on this: what's the advantage of following the virtual control tree approach compared to instead updating directly the controls that are displayed to the user?
- da_rob 2y agoFor complex scenarios (i.e. the user interacting with the UI while there are some long-running processes in the backround doing this, too), managing the state quickly gets unwiedly: You need to write callback code everywhere that carefully has to inspect the current status of all other activities and might then updates 10s of widgets accordingly. With the reactive approach you are able to write a single rendering function that describes the interface for any given state and the framework takes care of the rest (when to call that function, what "input" to give it). It's just way easier to wrap my head around this and after working with React it is hard to go back. Hence the experiment to see if something comparable could be done in Go.
- felipefar 2y agoI'm already convinced of the benefit of declarative GUIs over imperative ones. To be clearer, what I mean by declarative GUIs is ones where I can specify that UI elements should be bound to certain values in the memory of my program, so that they don't have to be changed explicitly by imperative code. Ex.: TextBox { Text: app.currentUser.name } Instead of having to call setText on that TextBox. What I'm still missing is why it has to involve a virtual tree being maintained and synchronized with the real GUI tree. The UI engine could implement the above binding by instantiating for the user a callback trigerred when the bound property changes and changing precisely the value currently shown on the screen. Hence, the UI engine is in charge of the callbacks, which keeps the user unburdened by callbacks.
- da_rob 2y agoThe virtual component tree is needed as there is no 1:1 mapping of components to widgets. Using the virtual tree, you are able to build components which dynamically change the rendered subtree based on the current state.
- zem 2y agointeresting that this uses fltk; I've seen very few language bindings to it
- andrewfromx 2y agoBeen using https://fyne.io/ https://fyne.io/ https://github.com/fyne-io/fyne https://github.com/fyne-io/fyne for years but will check this out ( terminal but still great go guis: https://github.com/gizak/termui https://github.com/gizak/termui https://github.com/charmbracelet/bubbletea https://github.com/charmbracelet/bubbletea ) Never used but should be in this list: https://gioui.org/ https://gioui.org/ https://mattn.github.io/go-gtk/ https://mattn.github.io/go-gtk/ https://github.com/lxn/walk https://github.com/lxn/walk
- bsimpson 2y agoIt's strange to see "inspired by Material Design," and then a bunch of controls that look nothing like Material.
- deleted 2y ago[deleted]
- iamcalledrob 2y agoI have been looking for something like this in Go for a while. I think there's a real opportunity for Go to provide a great developer experience for cross platform UI due to how simple the build process is. Speaking from experience, half the pain of cross platform development is managing build complexity, which Go basically eliminates. I'm curious how you'll end up solving for cross-platform layout when native controls have different intrinsic sizes per platform? This is something I haven't seen solved super well in cross platform toolkits. Wishing you luck though.
- kdma 2y agoJust for curioristy why did you adopot a react-like model and not implement react-dom as projects like react native do?
- bsimpson 2y agoReact Native runs in JavaScript. This is in Go.
- kdma 2y agoReact native runs in the native ecosystem through a js-native-bridge and calls platform specifics system calls. https://reactnative.dev/architecture/xplat-implementation https://reactnative.dev/architecture/xplat-implementation react is the <Components /> stuff in js react-dom is the juice
- sandreas 2y agoLooks great! Thanks for sharing. Would you consider listing the supported platforms in the readme? I think that would be a pretty interesting piece of information: - Windows - Linux - macOS - *BSD - Android - iOS - Web - Tizen Maybe like it's on flutter docs: https://docs.flutter.dev/reference/supported-platforms https://docs.flutter.dev/reference/supported-platforms
- Fire-Dragon-DoL 2y agoMain problem is that when you release on desktop, a web version is usually desirable (except for very niche apps that interact a lot with the OS). Alternatively, something that targets many platforms, including mobile. I've been searching for something for quite a while and the closest is qt and react native, both painful choices for various reasons
- gloosx 2y agoThis looks really cool but a bit incomplete and messy without XML-like syntax sugar for composing component functions. Is it even possible to extend Go like the JSX?
- jerf 2y ago"Cross-platform: Leveraging FLTK[1] & Cocoa[2], Spot works on Mac, Linux, and the BSDs with plans for native Windows support in the future." I'd seriously recommend that you consider cutting this. Perhaps keep the lessons you've learned to retain future flexibility, but get good on one toolkit first. GUI toolkits, GUI bindings, GUI in general drowns you in details as it is, volunteering to drown in several different underlying toolkit's details may sound appealing because you may feel like you're growing your metaphorical market, but what you will almost certainly end up with is doing all toolkits badly instead of even one toolkit well, and that won't be good. We've all heard about how the first 90% is 90% of the work, and then the remaining 10% is another 90% of the work. GUIs make that look like hopeless pie-in-the-sky optimism, where the first 10% is 90% of the work, and then the next 10% is ten times the work, and then the next 10% is another ten times the work. Trying to be cross-platform will strangle you. However, based on my experience with some similar previous discussions, I don't expect you to immediately buy and agree with my arguments. What I'll really suggest then is to keep what I've said here in the back of your mind, and when you find you're staring down the three toolkits that rigidly require a three-way mutually contradictory way of handling rich text or something, then think back to this post and consider giving yourself permission to drop all but the best-supported and/or most popular underlying toolkit.