Ask HN: How to make a native GUI with a modern language?

87 points by throwaddzuzxd ↗ HN
HN has strong opinions against Electron so here are my requirements:

- I want to make a native looking GUI

- Cross platform (macOS, Windows, Linux)

- With a sane language (no C, C++ or Objective C)

- Ideally with a data flow looking like unidirectional data flow / Elm architecture

What options do I have?

144 comments

[ 3.0 ms ] story [ 185 ms ] thread
Personally I like JavaFX.

I made something pretty complicated with tkinter and Python. It's not that hard to do but the result looks awful, it doesn't support many features people expect in 2022, and the documentation for using tk in Python is poor. You probably need to refer to the tcl/tk documentation and translate it in your head to apply to Python.

As much as people hate Electron, the rendering engine in a web browser is head and shoulders better than any other cross-platform system and I think it's a sane model for programming. The main trouble w/ Electron is that you have 30MB of runtime for any application, even if it something that could be coded in a 25k .EXE file based on Win32.

quark / sciter isn't OSS, and I've never used it, but they claim they can bundle something like electron (JS backend, web frontend) into a 5mb binary
I haven't used it myself (yet), but https://tauri.app/ looks very promising as Electron alternative.

It uses the OS's browser so it doesn't have the bundle size issue you describe.

Isn’t the downside to this then that although you’re using the native browser you’re losing the whole point of Electron which is to have a consistent environment regardless of where it’s run?
That is a downside. However, it's better to look at Tauri like you might look at React Native. If complete consistency between platforms is required, Tauri still saves you time by letting you write a single codebase and then do a low effort port to each platform. For many applications, Tauri applications will look the same between platforms out of the box.

That's the pitch anyway. People can spend a lot of time on ios vs android in react native applications, so ymmv

Probably. But I don't think that is very problematic.

Browsers have become a lot more standardized. What also helps a lot is that windows is now Chromium edge instead of IE or old edge. Testing tools have also come a long way.

It's the same as building web-apps.

Specifically Tauri uses web views that are based on Chrome or Safari on systems, and only if they don't exist by default will it package it, which somehow still ends up smaller than electron. But on Mac and Windows it is tiny, and on Linux it is still surprisingly much smaller than electron, but it is a consistent environment.
I've tried it. It's a very strong effort but if you want to support Windows then welcome to hell.

You'll need to ship Microsoft's new Edge runtime thing, which is a separate package. You'll either have to include that giant package in your package or download it on demand, and the latter is very brittle and will fail if MS changes their URLs or you're installing inside a very heavily firewalled (whitelisted) environment. If your product runs in enterprise settings you will get complaints from IT people about how your single installer installs a "hidden" second installer that they don't want, especially since the Edge runtime runs some kind of system service (why?) all the time. You'll have to basically take over maintaining up to date Edge runtimes in your bundles and handling edge cases because Microsoft can't ship software for their own OS. People will complain that if they uninstall your app it does not fully uninstall because it leaves the Edge runtime.

Tauri just works everywhere else but the headaches on Windows defeat the purpose to some extent.

Of course I guess shipping anything for Windows is "welcome to hell." The only way to have a package that installs and runs reliably is to bundle Every. Single. DLL. And. Dependency... or statically link it.

As another Windows application developer targeting enterprise users, thanks for the warning. Electron it is, I guess. Either that, or using the IE engine for web views, or no web views at all.

Edit: I'm already using Electron, though I've been wishing I could move away from it.

If you are shipping software for Windows just make sure your insurance has good mental health coverage.
Heh, I've been shipping software for Windows one way or another for about 20 years. I just hadn't used Edge's WebView2 in anger yet, and now I probably won't.

Edit: And I've generally followed the "bundle every dependency" approach. As far as I can recall, the only dependency in a shipping product that I ever delegated to an external installer was the .NET Framework (before the .NET Core reboot). And IIRC, that did trip up a few users; thankfully, that was only a consumer app.

You should verify the original commenters statements hold true in your target environment(s). Office has started heavily relying on WebView2, any enterprise using Office should have WebView2 updating working properly. Ditto for Windows 11.

Not sure what time period the original commenter is referring to, this story has developed quite a bit over the past year, even into last month - https://blogs.windows.com/msedgedev/2022/06/27/delivering-th...

Edit: and if your alternative is Electron, WV2’s fixed runtime is the same concept as Electron in a static Chromium version - https://docs.microsoft.com/en-us/microsoft-edge/webview2/con...

I don't think this is accurate as Windows 10 and Windows 11 have edge built in, as WebView2, which is used for some internal Microsoft programs now as well, and older windows systems are not supported in any way by Microsoft themselves anymore, so it seems safe to ignore them now. I've had no issues with Tauri on Windows however, and I even compile it from Linux.
> trouble w/ Electron is that you have 30MB of runtime for any application

JavaFX has the same problem? Or you rely on user's JREs already installed? I'd think 30MB really isn't much any more.

The difference is that Electron is bundled with every app.

If you have 200 electron apps, that’s 200x30mb.

With Java, the JRE is reused so the binaries can be smaller.

That is the case with JavaFX as well. It's not part of the JRE anymore. Also there really isn't any such thing as the JRE anymore, only the JDK. If you want to distribute a desktop Java app you must embed the entire runtime via javapackager. They have correctly realized that end users cannot be expected to install and update a language runtime separately...
First, of course, all this depends on the audience.

JavaFX can still be installed in a JRE, so that the application does not need to bundle it. This is fine for internal distributions, or distributing to people willing to put up with the minor headaches of maintaining the JRE separate from the app.

But, consider, for example, NetBeans does not come with its own JRE. It's not a great leap, since it's a first most Java IDE, but if you wanted to use NetBeans for PHP, you'd still need to install a JDK separately just to run NetBeans. NetBeans doesn't use JavaFX, the point simply being that there are still large Java apps out there that require you to separately download the JRE/JDK. How much this is a hinderance to their adoption, I can't say.

The direction is towards all in one JRE/FX/App bundles, but that doesn't mean it's the only way.

If you can just deploy the app jar file, the apps can be quite tiny.

In the middle ground, https://jdeploy.com manages this. They host applications, but as I understand it you can deploy the infrastructure yourself and host it yourself.

You download the app, and their infrastructure will download the appropriate JRE (including JavaFX), and share it among all those apps that do, indeed, share it.

It has "up to date" systems for updating your app. When you download the app, you get an installer that does all the work.

This is great if you have a bunch of small apps you wish to distribute, as they're basically just the jar files. The first install is a kick, of course, and as you move through the JVM versions, they'll all incur an additional hit.

But if you distribute a dozen apps all on JDK 17, then the overall download should be quite small as the JRE is only downloaded once.

I have yet to use it, but it demos nice.

As for Fonts, they seem OK to me. I use macOS, but I've also looked at them casually on a Linux VM. However, there is one sticky bit with the fonts.

At least on macOS, the default font is terrible simply because, of all things, it doesn't support the different font faces. You can't do BOLD with the stock font. It's quite the nut until you figure that out.

So, I ended up embedding a font in my app. And it all worked. It rendered fine on my Mac, and the Linux VM, and it printed to the printer well as well.

I chose Source Sans Pro, which is one of the available Google fonts.

It's annoying that I had to go down that rabbit hole, but it is what it is.

There's some ramp up time with FX to be sure, but the combination of FX with the SceneBuilder to do layout works.

This is a recent screen shot of my app, I'm no UI guy (I majored in "Programmer Art" in college...), but it looks OK to me.

https://capture.dropbox.com/AuXy4deWZTAHGHk7

This is the default theme, there are others. The CSS part of FX is really nice.

I hold FX in high regard. I've certainly had my share of head flattening desk denting events with it, but it's nice, it works.

(comment deleted)
A browser is much bigger than a runtime with a few packages though.
Not anymore.

To put things in perspective, my first hard drive, back in 1989, was 30 megabytes. I think it did about 500Kbytes/sec for reads. I also had a 2400 baud modem.

Today, 30 megabytes is a 1 second download on a bad day.

I used JavaFX a while ago and hated it. It was insanely slow, fonts were awful and the lib of components was non existent.

It really felt like something created by someone but never used in the real world. Is it even used anywhere?

Agreed, JavaFX's font rendering on MacOS was like this for years: https://user-images.githubusercontent.com/1413266/82507544-6... (Note not only the inconsistency between letters - all the specific colour values used are the wrong ones to get a working subpixel effect in the first place).

Possibly still is like that. But that gets around to your question... is anyone still around to notice?

JavaFX is not native, nor can it be made to look native like Swing can be made.
Why couldn’t it be native (whatever that even means)? At the end of the day, it does have a canvas you are free to draw anything on, so there is nothing you can’t imitate.
Native would be Win32 calls to draw widgets. There's a built-in Swing look-and-feel that uses this API to render widgets in Swing. Yes, you could write your own code to replicate how things look, but it would be fragile and difficult to maintain. With Swing, it's built in and already maintained.
I'm mainly a java developer, but I hate the built-in Swing gui. I tried once JavaFX for a medium project and quite liked it, specially since you can use Kotlin with it and also because you can compile it to a portable executable easily (with a built-in jre, not an exe but at least you don't need to install anything).
JavaFX is terrible and I'm a Java fanboy. It isn't native looking so it doesn't satisfy the requirements outlined above. You can get a facsimile of "native" but at a lot of effort and for specific cases.

But the problem is the deployment story. It requires native libraries so you need to package it somehow. Ideally, one day GraalVM will support JavaFX. That day isn't here. So you need JavaPackager which is one of the more awful ways to deploy anything at scale.

It pains me to say it as a Java fan, but most people will be better off with Electron.

There is no such thing as native, windows doesn’t even have a singular native look.

And as far as I know jpackage and the like should be able to package a javafx program just fine.

There sure is "native look" for Windows and Mac. Did you actually try shipping an app built on such a packager?

I shipped multiple. JDeploy solves some of those things but it's not as refined as native packaging.

What's the problem with jpackage for, say, shipping Windows binaries?

I just let it package a "portable" (no need to install) directory of all dependencies and that's it. Users just download a ZIP and can use my programs.

They don't have FX installed. No one does.

The whole idea of using Java is to support something other than Windows. I use a Mac and all companies I worked with had all common OS's on desktops. The edge cases are the problem. This isn't a product for other engineers. At least not engineers who have experience with JVM conflict issues.

For end users and to get a smooth installer experience there's a huge distance. Customizing the installer in the packager is a nightmarish problem... Or at least it was the last time I did it.

I can't use jpackage on Windows to build Mac binaries, but not having FX installed is what kind of problem? Just use jpackage to create an .app for Mac, an installer or portable build for Windows, and a .deb or executable binary for Linux. It bundles all dependencies and users can just click and run it.
That's indeed what we do in our CI but it gets very hairy and the end result is often subpar in nuanced ways. E.g. signing a bundle which required us to upgrade our whole CI infrastructure because we had to move to a newer VM.

The bundle is huge because it needs to include a JRE and JavaFX. The install process and bundle building is complex. Especially with signing. And you need to do it all for every platform.

I see, thanks for the insights.
Definitely Flutter. https://flutter.dev
Though, your milage may vary, as you still need to

1. Take care to ensure that you follow platform guidelines

2. Ensure that the view and controller are not tightly coupled

3. Ensure that whatever libraries you want to use have support for all platforms you want to support

4. Pray that you don't need something very platform specific (or need to use Bluetooth, which I am told is a huge pain)

Hate to say it but, this is one of the biggest tragedies of modern CS. Best bet is to use gtk, or qt, and hope the language bindings for your lang of choice aren't horrible (depending on the language you might be in tears)
Can you elaborate more? I actually don't quite understand this statement.
Most computer scientists I know barely care about anything graphical or user-facing. I don’t think this is a huge tragedy to them, at least when compared to software developers.
Why the emphasis on "unidirectional data flow"?

I happen to think Lazarus (based on Free Pascal) is great, but it's not a functional language at all.

I find it easier to reason about.
Qt for Python may be an option: https://doc.qt.io/qtforpython/

While the Qt API is imperative (`self.layout.addWidget(self.text)`, ...), focusing on declative Qml whenever possible helps with a more declarative approach.

If you don't think C or C++ are sane languages you are never going to write any thing sane for Von Neumann computers imo. Much less something cross platform.
Ok, so be it. C and C++ are still insane to work with.
It's really not that bad and comments like this just perpetuate the fear to other people who have never touched them.
I think possibly "perpetuating the fear" was the intent. I'm not siding with OP as I don't use C but if they think it's a bad choice then they probably want to assist other people in avoiding that choice.
I honestly think it is that bad.

Heartbleed was reviewed twice by literal OpenSSL experts and security experts who were specifically looking at it to determine whether it was a security vulnerability or not and concluded (incorrectly) it was not. And now it's the poster child for bad software bugs.

It's really hard to write correct software in any language, and time has shown again and again that memory safety is just not something you can hope to do without if you care about security in the slightest.

Whenever I see a small comment like “C++ bad, X good” I wonder if the poster has a thorough understanding or they just hold the opinion because it’s fashionable since they heard it so many places prior.
Not quite sure what your definition of a “sane language” is. Pretty much all guis in the industry involve one of those three to some extent.

Maybe look into Skia, it’s Googles abstraction layer for multiple graphics libraries on multiple platforms.

But if you are just learning, I wouldn’t concern yourself with any of those sort of environment conditions, just figure out how to make something that works on your preferred OS.

For example, if you use Windows, learn 2d rendering with direct x. Learn about event handlers for controller interaction. And of course read about MVC.

Are C# WinForms or VB.NET not the right answers here for some reason?
I don't believe mono supports winforms on linux and I wouldn't classify VB.net as having a elm-like architecture with a unidirectional data flow... so yes?
WinForms will only look native on Windows. On cross-platform it generally looks like a mess. Also, even on Windows it may look native but it also looks "dated" generally.
Depending on your application, write as much of the core functionality as possible into a cross platform library and/or server in your language of choice. Expose all functionality through a strong API.

Now you can build completely native GUIs in the appropriate language for each platform. Start with the platform which will likely have the most users for the application. Bonus if the application is open source, other developers will build all kinds of native GUIs for you. Just look at how many GUIs are out there for things like PostgreSQL, Git, IRC, etc.

I know this doesn't exactly meet your requirements, but I think this is the way to go if starting something in August 2022.

> HN has strong opinions against Electron

Beware the bias of the loud minority. For every 1 noisy user, there may be 50 quiet users who are fine with the decision to use Electron.

Shipping beats not shipping every time, and if Electron is what you need, then do it. I say this as someone who does not like Electron, but also as someone who knows that writing comments on HN is 100x easier than actually shipping production software.

> HN has strong opinions against Electron

Beware the bias of the loud minority.

Hear hear.

Though depending on the application(s) in question, if JS and things that traspile to it are attractive to the developer then they could also consider an offline-first/-only web app. Not practical for everything and adds its own family of potential complications, but worth considering.

> but also as someone who knows that writing comments on HN is 100x easier than actually shipping

As someone who has a huge pile of projects just waiting to be started (and one or two languishing in a PoC state) I think your 100x factor is more than a little too small!

I usually agree with this sentiment of shipping vs not shipping. But Electron is just really bloated. You cannot install 10 apps and regularly use it. Most systems will just hang or crawl. Systems with 8GB should not be hanging because you are using 10 apps.

I have 16GB RAM and even then I feel the hog with Discord, Slack, VS Code, A note app taking up too much memory. Then there is unnecessary security risk of bringing all the browser issues along with it.

People shouldn't have to buy an expensive Mac books to use normal apps. That should be for MLOPS or other processing heavy tasks.

Also, an Electron app uses anywhere around 150+MB to 400ishMB ram on normal cases. For one app.An app like QOwnNotes uses only a small portion of it. 50MB-100MB max. So when you you add just 10 electron apps, the number just blows up.

I feel like whatever progress with performance we are making with processor is just cancelled out with things like Electron. That is just sad.

So no. I don't think this is a good example of loud minority scenario.

It is sad.

But I'd rather people ship something and refine the Electron version than never ship anything at all. Mostly because I believe more in indie devs than I do in the purity of software.

I would at least recommend trying tauri.studio out instead of Electron. :)
As an indie dev who has been working on an Electron application, thanks. Reading comments on message boards like this one, it's easy to pick up the attitude that it would be better to have less software than more crap. And I think that internalizing that attitude hurt my productivity on this current Electron-based project. But once I was able to push through that and get the app out to actual users, I see growing enthusiasm among the target user base, and as far as I know, zero complaints from actual or even potential users about the fact that the app uses Electron. Sure, maybe those complaints will come once the product gets past the early adopter phase, but the reception so far has been encouraging.
The secret is to have your employer buy you a brand new top spec MacBook Pro every two years, then it's fast enough and doesn't bother you. That's also what all the Electron devs are doing.
Does the "native GUI" requirement not exclude Electron, though?
Part of me wonders why someone hasn't tackled creating an Electron runtime service, so that each installed app doesn't load what could be a shared library into memory. There's a host of complicated problems there, but if the major issue is fat runtimes it can be pared down like Chrome workers.
I hate to say it, but I agree - I dislike the number of Electron apps I have to use, however...

I spent a few hours last night on a personal project trying to get something relatively simple working as a SwiftUI macOS app. I needed to customise a bunch of components to get it close to what I wanted.

I decided to give Electron a go, and had what I wanted up and running quite literally in 5 minutes, and it starts and is responsive within 500ms. It's taken longer to figure out how to ship it as a .app package than to write the code. That's nothing short of amazing.

The "minority" may be loud but their comments are usually heavily upvoted.
(comment deleted)
I hate to stick my neck up here but looking at this recently, I think that in trying to get cross-platform, you might end up with a lot of pain and not that great results. Instead, I would go for getting 3 devs and writing an app in a native language on each OS. You can refactor the business logic into a dll using some standard languages like C/C++ (they aren't that bad) and possibly Python and then just build the front-end in the native framework.

Why? The only way to get a truly native look and the ability to specialise for each OS. Maybe Windows does threading better than Linux and can be optimised in some way. Maybe Linux has some nicer native UI controls that could make that part better on Linux.

Otherwise cross-platform mostly means a UI that is not native looking on any platform. Haven't used QT recently but of all I used, that was the closest to what you are asking for (I was using C++ - sorry, it's not that bad!) but I think there are bindings in other languages, although they might well have some bugs compared to the basic C++ version.

I agree with this (except for the bit about C++ but that's neither here nor there) but what I landed on was that maybe a native look isn't as important as I thought. Some of the nicest apps I've used just have their own look. They're still native and very quick, but they use their own UI toolkit that doesn't fit the system. For example nheko, ripcord, blender, sublime merge, etc.
If you go this route I would mention that I had a really good experience creating a C library in Rust. Lets you use a modern language for your shared logic but still be very portable. I believe you can do the same thing with Zig and maybe Nim as well. And there are probably others
Flutter seems like the strongest option right now

Though overall, its not an uncommon issue. I've found myself in the same place multiple times, I've even considered using a lightweight game engine just for their UI features. But next time I run into it, I imagine Flutter will be what I go with

Edit: Just realising you said "native-looking". Not so sure about Flutter for that, I'm not sure such a thing exists.

There are UI packages for desktop OSes with the native designs.
I mean, how native are we talking? Cross-platform native? Or platform native? Platform native UIs require you to actually read things like the Apple HIG, or the Windows Design Guidelines, and then create language-specific bindings to those operating system controls.

If you don't actually care about creating platform native UIs, you'll always be creating something that is a second-class design while putting in first-class effort. Why?

Frankly, you're better off just learning the operating system platform native UI programming language and get over yourself if you want to create something that looks like it was actually designed for the OS you're using.

And really, it's immature and insulting to call languages insane. Suck it up, these are peoples' professions here. No one wants to read that.

Objective-C is not sane? Have you ever tried it for anything non-trivial? I know that it's not perfect and it's syntax is unappealing to some. I did iOS development for a few years (pre-Swift) and I really enjoyed using Objective C. I found it to be efficient, intuitive (once you get acclimated), and very pragmatic.

There are some very powerful capabilities in Objective C (like swizzling) that can be of tremendous help when you have some oddball functionality to implement. This would be conceptually very similar to the monkey-patching that folks do with Python. Perhaps this is the sort of thing that makes it not sane to you?

I write native GUIs in GTK using Julia at the moment. Julia is one of the most modern languages you can use today. Multiple-dispatch, very functional, modern package management and virtual environments, high performance, modern sophisticated REPL environment. GTK is quite nice as Glade is one of the nicer GUI designers.

Here is a simple Julia GTK project on GitHub: https://github.com/ordovician/RocketBuilder.jl

Some articles I wrote related to GTK programming with Julia and GTK in general:

- Understanding GTK Layouts: https://itnext.io/understanding-gtk-layouts-13e5a36256fa

- Hiccups Writing GTK3 GUI Code in Julia: https://towardsdev.com/hiccups-writing-gtk3-gui-code-in-juli...

If Julia is not your cup of tea then GTK can be used with Go and Swift as well.

The big problem with GTK is backwards compatibility or, more precisely, the lack of it. I don't know how it has or hasn't impacted your own work, but I would hold off on recommending GTK to OP unless he/she is aware of this caveat.
Have you tried / had good luck doing static compilation of Gtk applications?
Does GTK run on windows and macOS also?
Having a native looking GUI and being cross platform is almost contradictory. The only way to make truly native looking and feeling GUIs is to use native tools. That means Cocoa on macOS, Gtk or Qt on Linux, and Win32/Winforms/WPF/WinUI on Windows.

Most cross platform frameworks won't really look 100% native. The only exception I know is wxWidgets, which provides an abstraction layer over the native toolkits. It's written in C++ but there are bindings for Python, Ruby and other languages. But even using this, apps might not feel truly native. There is more to nativeness than looks. It's also about subtle conventions and ways to do things. You only achieve this when you use a native framework and pay a lot of attention to the HIG guidelines of the OS.

My personal (and perhaps a bit unpopular) opinion is that native looking is a bit overrated. And it's mostly macOS users who look for this. Linux and Windows users already get a lot of diversity in their looks and feel because there is no single framework for those OSes.

More important than native looking is to have a GUI that is fast, efficient and accesible. The best option that provides this in a cross platform way is QT. Java Swing and JavaFX are also good options.

The most productive tool to have cross platform GUIs right now is Electron. That's just a fact. And it's the reason why so many modern products are using it. But as we all know, there is a price in efficiency.

There are a bunch of new projects in the works to improve cross platform GUI for modern languages. JetPack Compose Desktop, Flutter, Slint and many more. But these are not yet fully mature.

> More important than native looking is to have a GUI that is fast, efficient and accesible. The best option that provides this in a cross platform way is QT. Java Swing and JavaFX are also good options.

I would echo this. Consider applications like Photoshop or Blender, these don't really have a 'native' feel anywhere but their UX is generally responsive. On the other hand you have Electron applications that don't feel native and are also sluggish...

What does Photoshop use?
As it’s very old, probably an in-house framework.
FL Studio is written in Delphi and the UI is very snappy and modern with a lot of animations.
Delphi always had a very good and fast UI library, the only complaint in the older days was that it made for big binaries because VCL would get statically linked
Yes, but a big binary means 1MB. Nowadays a Helloworld webapp needs more.
Blender's UI is rendered with openGL and it's glorious. Photoshop is getting really slow in my opinion since 2015. I see all those Chromium processes running with it and wonder just how much of its stuff is being replaced with this slow shitty code. Everything 'modern' that has rounded edges feels just slow and laggy.
Besides wxWidgets there's also SWT for Java which also wraps native widgets.
The other one is Eto.form for .Net and .Net core. Same concept as WxWidgets.
Strange nobody mentioned Tauri, it is really pleasant experience to use and good solution. It works really well for what it does, it is still not covering all features and everything, but it is more performant and more secure alternative to Electron.

I was considering using it for some app that otherwise would be PWA. While I didn't use in-depth features and didn't run into any serious problems, it is likely that not everything will be perfect, but it is also likely that overall experience will be more pleasant.

https://tauri.app/

Check it out.