Hacker News new | past | comments | ask | show | jobs | submit login
Clojure on the Desktop (vlaaad.github.io)
300 points by vlaaad on March 31, 2020 | hide | past | favorite | 134 comments



I once led a mid-sized team in building a desktop application with Clojure. It might sound nuts, but we used both the JVM and Electron, using both Clojure and ClojureScript. We solved all the performance issues we’d experienced with Electron by restricting it to serve as a desktop GUI API. Literally nothing was in Electron except a small package for handling these API requests: set a menu, open a window, load a page, etc. Even inside an open window, the ClojureScript code was loaded from and communicated with the JVM, rather than being bundled into Electron. You want your desktop app to both look great and perform great. Restricting Electron to its basics while moving everything else into another system is definitely one option to consider.


This reminds me of this writeup on doing something similar, with Rust as the native core for an electron app; the Rust in this case is in-process, written using "neon".

This app (Finda) hit a performance target of 16ms rerenders for incremental browser history + desktop file search - computers are fast, even if you use Electron.

- https://keminglabs.com/blog/building-a-fast-electron-app-wit...

- https://github.com/neon-bindings/neon

(edited for formatting)


I am actually currently working on something similar but instead we opted to use JxBrowser[1]. It's basically what Electron used to be but has Chromium embedded into the JVM instead of Node.

[1]: https://www.teamdev.com/jxbrowser


Super cool. If someone wraps this with Clojure for the backend, ClojureScript for the front-end, and hides jpackage and NPM (or whatever ClojureScript uses) details, this would be a great Electron alternative.


I honestly cannot imagine how that would be more performant than just using Electron by itself the typical way, unless this was a very CPU intensive application like a video editor.


Last I checked, due to how Electron’s structured, you basically have to decouple “logic” from the UI anyway, which makes it relatively easy/natural to make the “logic” side just a thin translation layer talking to some other language.


not sure what you mean here. You can write a full fledged app in Electron without any 'backend'


Last I checked it runs a process (I think a full process, not just a thread?) for the UI and another for the rest of the application, and the two talk via calls to IPC functions.


Why did you guys use JVM at all ?

If it's for multi-core computation purpose it can be with Worker Threads in JS or CLJS.


Spawning a thread is much cheaper than spawning multiple processes right?


Was webassembly considered before going with this approach? It's completely okay to go in a way the team is most comfortable with. But if it was pure perf reasons then webassembly would solve it right?


What did this do and why was using multiple languages, an entire web browser and the jvm a good fit?


Most of the comments are about the startup RAM use and distributable size, which is a criticism shared with Electron. The demo app is not attempting to address these particular weaknesses.

The point of the article is there are some applications where processing speed is important where Clojure's and JVMs concurrency and long running and large dataset performance is very important. And the ecosystem appears to be ready to make it possible to create apps where these strengths vis-a-vis Electron are combined with the other strengths of Clojure compared to low level languages (expressive syntax, interactive development).


not just that: we've come full circle. At one point, there were a bunch of Java/JVM desktop apps. They were widely disliked/mocked because they were heavyweight, had slow startup time, didn't use platform-standard widgets, etc. The web became an alternative to cross-platform apps.

Now we're seeing a proliferation of Electron apps - web apps - that are widely disliked/mocked because they are heavyweight, don't use platform-standard widgets, etc. And at least in this piece, an alternative is the JVM.


Yes the initial hype on Java when it came out of Sun was all around desktop use, often but not exclusively in browser. They even tinkered with the idea of writing the browser — even an OS! — in Java. There was literally something called JavaOS. Anyone remember the JavaStation?

Trouble is the computers of 1996, combined with the less optimized JVM of 1996, were really not up to the task. And I would venture the GUI libraries were not there yet either. 24 years later we have very speedy JVMs and much faster computers and networks and toolkits like the one in the article (JavaFX) sound much smoother. Meanwhile Clojure offers concurrency primitives that are making threading much more accessible.


Yup, and since VSCode and IntelliJ both used too much RAM, I needed to switch to Emacs on my old dell laptop, yet it used to be that people mocked Emacs for using too much RAM, in fact, the joke was that Emacs stood for: Eight megabytes and constantly swapping.

The wheel just goes round and round :p


Unfortunately I am old enough to remember starting emacs remotely on a fellow student's computer so that he stopped working and went to the restaurant with us: starting emacs 'froze' the computer and made it unusable for a (small) duration.. So there's some truth behind the joke: vi started much faster. So I never learned emacs which I regret now because I feel that emacs is much better as a C++ IDE than vim.


Yeah it's really interesting because it's a fully worked example of packaging a desktop application using a hosted approach. Personally, I'm more comfortable with that stack.

Also worth looking at is Vlaaad's Cljfx (https://github.com/cljfx/cljfx). And I just want to say the number of examples that Vlaaad has created for the project is amazing and really helpful - it's a great example of helping others on-board to use your project - thanks Vlaad.


Yeah I'd say the point of the article is to highlight the Clojure wrapper for JavaFX, providing an option to develop JavaFX desktop apps in an Om/Reagent fashion and playing to Clojure's strengths.

All the criticism here seems more like criticism of JavaFX itself.


I routinely run my fairly and complex java UIs clamped to 256M of memory, and startup time is measured in a handful of seconds. Distribution size is not great I'll admit, unless you go to considerable effort to slim the JRE down.


I feel distribution size like, users should have a JRE on their machine. Same way native apps expect you to already have a bunch of existing modules already installed as well. Or at least, if many apps were all using Java, you'd only install a JRE once, and then keep getting the non JRE bundles, so size of distribution would be a lot less.

What I mean is, at least for Java apps, this is a possibility. In Electron world, even though almost everyone has Chrome installed, you can't share it, you need to keep bundling the whole thing over and over again.


IME, regular JS is much faster than regular Clojure. You can make Clojure run faster than JS by writing Java with parenthesis, but then you defeat all the purpose of using Clojure in the first place.


That is a weird claim to make and completely not true in my experience. Idiomatic Clojure is faster than any JS except maybe asm.js-like stuff.


Clojure has always been measurably faster than JS for me on every use cases I've ever used it for.


I am very curious about your experience. Could you provide some more details?


Hmmm... Lets see the HN Reader example then...

95MB for the macOS DMG.

177MB for the actual example app.

For a typical hello world application like the HN example, it appears that it would be no better than running an electron-made version given that they both include their own versions of their respective runtimes. In this case, this includes the entire Java runtime which Clojure needs to run.

I think I would be better off with using either C++ with Qt5 or even FreePascal with the Lazarus IDE for this instead.


As suggested in the article, the usage of jpackage comes with a lot of drawbacks:

- still a JRE is shipped (you MUST tailor it to the bare minimum with the flags) -> can be + ~30-40MB

- OpenJFX/JavaFX relies on Qt (especially for the WebView) -> can grow up to + ~70MB

- all the legal documents and docs are shipped together (you have to remove them with a post-process script) -> + ~5-10MB

Using JVM for desktop apps is expensive in terms of space. Time can be reduced consistently thanks to the graal native compiler, but still a C++ project outperforms Java GUI applications by 500%


OpenJFX/JavaFX uses WebKit for the WebView, Java never had any dependency to Qt.

https://github.com/openjdk/jfx/blob/master/modules/javafx.we...

I wonder where that 500% number came from, and yes C++ does blazing fast against Java in micro-benchmarks, except that on project delivery, developer salaries, time to market, additional costs for bug fixes (including memory corruption ones), available libraries, hardware costs for build performance also play a very big role.


Until Java 9, openjdk was requiring Qt 5 for OpenJFX because of the WebView, I clearly remember that. Still, shipping WebKit is pretty consuming


https://docs.oracle.com/javase/8/javafx/get-started-tutorial...

> A web component that uses WebKitHTML technology to make it possible to embed web pages within a JavaFX application.

Qt was indeed there, just like Gtk, EFL and every other plaform that is part of the WebKit source code vendoring inside the Java source code tree.

http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc9...

Which doesn't imply that the native code shipped with Java has any trace of Qt inside of it.

https://www.oracle.com/java/technologies/javase-jdk8-downloa...


Consider my mind a little blown, however this seems to suggest that it's no longer the case: https://github.com/javafxports/openjdk-jfx/pull/48


It was part of the WebKit vendoring and build process, as you can see in the source code, but it wasn't shipped with the JVM.

http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc9...


Indeed but Java Isn't that better compared to C++ when it comes to all those factors. There aren't memory corruption bugs, but the average number of Null pointer exceptions in fairly big projects can be daunting and relevant.

Of course, Rust and Go here a big game changers in this way


Since we are speaking about desktop apps here, Rust and Go still have to offer something that is half as good as Swing, including graphical design tooling.


I thought Rust desktop UI framework/libraries etc are still very much a work in progress.


Exactly, which is why "Rust and Go here a big game changers in this way" doesn't apply in this context.


give them some time. And there's a Go API bridge for Qt


So the proposition to have a better UI done in Go to beat Java is to use C++.

Well, I can also call C++ from Java.

In fact, once upon a time Qt had official Java bindings.


I agree with you, having a poorly documented qt binding which may break on any update is not the ideal way for a modern language to have GUI functionality. Java is good in the sense that it has JavaFX, a modern and rather full featured GUI framework available for it.

Rust, Go, etc is very far from being recommended for production desktop apps with GUIs.


Graal won't make apps run faster. The responsiveness of most Java desktop apps isn't CPU constrained, like on the web, slow reactions come from elsewhere. On the web it's mostly the heavy reliance on the server to do basic tasks meaning there's roundtripping. On desktop apps it's mostly like on mobile - if you hit disk or do other slow operations from the GUI thread then you can get "jank".


Android here took a good approach actually, since developer education and lint checks apparently weren't enough, eventually Google changed the OS to kill an application that does such requests on the main thread.


I tend to think rigorous constraints are important to achieve software quality because when something stupid is possible someone's going to do it ("what's the problem? it works").


Kind of surprising then that not a single major 3D, music, office, or photography production software is opting to use web technologies for the GUI on the desktop.


On the web it’s mostly putting JavaScript between input and reaction/output, leading to input lag, and devs paying no attention to how much memory all those convenient JS data structures will eat and how slow it is to do stuff to them over and over. A few ms here, a few there, a ton of objects and lots of string concatenation, and boom, you’re using 1.5GB and your app feels slow as hell.


I wonder if something like https://github.com/zserge/webview but using Lua for the backend side (to avoid the need to recompile it) would be lightweight in just the right ways while still being (almost) as good as Electron?


Those other systems don't have Reagent. Reagent is simply the best way to write user interfaces I've seen in 25 years, bar none. The only comparable jump in productivity that I can recall is moving from assembly language to a compiler.

Spending time with a profiler, we can probably improve the size and speed of a Clojure application (I've never tried using it for desktop software). No amount of time playing with Qt/Lazarus is going to give you Reagent, though.


So, you are saying that this :

    (ns example
      (:require [reagent.core :as r]))

    (def click-count (r/atom 0))

    (defn counting-component []
      [:div
       "The atom " [:code "click-count"] " has value: "
       @click-count ". "
       [:input {:type "button" :value "Click me!"
                :on-click #(swap! click-count inc)}]])
Is better than this :

    import QtQuick.Controls 2.3
    Button {
        property int count: 0
        onClicked: count++
        text: count == 0 
              ? "Click me!" 
              : "The value is : " + count
    }
? (testable here : https://tinyurl.com/rjgcfod)


I'm confused. Clojure doesn't have Reagent either. ClojureScript does. And Reagent is a pretty straightforward React wrapper, not so different from Rum. What do you think makes it special? Even Rum renders server-side.


The author of the post is the creator of cljfx https://github.com/cljfx/cljfx which basically allows you to use the reagent model to build JavaFx GUIs. I believe that is what parent is referring too.


~5 seconds from double clicking the app to the window appears (and starts to load HN) on my 2019 MBP.

I think this is a really cool library. I use clojure daily and love it. But this app isn't a very compelling demo over electron atm.


Startup time can be made more or less instant with AOT-compilation of Clojure which is out of the scope of the example — it's purpose is to show packaging capabilities.


how much extra work to add AOT step to the packing process?


Haven't tried that, but the actual compilation is something like this: you need to create `classes` folder, add it to the classpath, call `clj -e "(do (compile 'hn.core) (javafx.application.Platform/exit))"`.


Good point.

Lately I have been learning Swift and SwiftUI and application sizes for macOS and iOS tend to be very small.

Except for not being a lazy evaluation language, Swift development feels a little like Haskell, which to me is a good thing. I learned Swift back when TensorFlow for Swift was released and having one language that is adequate for many types of applications is useful. That said, when I want to be happy programming I try to use Common Lisp or Racket. I am approaching my 40 year anniversary for using Common Lisp and there is definitely something really great about deep familiarity with a language and it’s dev tools.

EDIT: also +1 for FreePascal. Someone hired me to use FreePascal on a project several years ago. Good language, small learning curve, and small and efficient applications. On small application sizes: I recently discovered that if you build SBCL Common Lisp from source, there is a compression option for building standalone applications that you can enable that gets application size down to about 15 MB, with application startup around 75 milliseconds.


Interesting, but I wonder if Swift and Swift UI isn't cheating, since I'd assume most of their runtime is already present on iOS and macOS for them to link against. This is also often true of all these "native" solutions. It's more that the toolkit and the runtime is bundled into the OS they target, where as Electron or Java apps bundle absolutely all their dependencies including their own runtime. Though with Java, I think you could assume the user would install those separately, and you could just to use the existing ones.


This example might not be the best with regards to application size because it's main purpose is to show that packaging is now easy. As you can see in the app itself or on the screenshot, it also includes browser to render linked pages — but that can be excluded if the size is a concern.


>> I think I would be better off with using either C++ with Qt5 or even FreePascal with the Lazarus IDE for this instead.

Or many other things. Not sure why are we so afraid to develop native applications with minimal overhead. V and Zig is going this direction.


I've installed the application, it needs 840 MB of the memory. Firefox with more than 100 tabs opend consumes 1.2 GB, IntelliJ with a huge java project consumes 1.6 GB. I understand that this is proof of concept application, but what eats so much RAM? Is that the data that was read or the framework itself?


It doesn't actually need 840MB of RAM. This is an inherent limitation of the way the JVM works. It will reserve a configured amount of memory upfront and grow the heap if memory is full until it reaches the configured maximum heap size. One especially annoying thing is that the JVM will not garbage collect until the heap is actually full. This means if you allocate a little bit here and there (like every single Java program in existence) over time you are going to reach the maximum configured heap. Once the JVM has allocated heap space it will never let go of it. Therefore you might as well assume that the minimum amount of memory a JVM process consumes is the maximum that it has been configured for. In this case the maximum was probably set to 840MB. The application itself probably only uses something like 50MB.

Remember those stupid arguments that a competent programmer can write well performing code [0] in any language? Here is the thing... You actually can't. Especially not with the JVM. Let's say you use a super efficient framework that is purely optimized for startup performance and low memory like Micronaut or Quarkus. When you look at your metrics you see that the Java program is consuming 10MB of heap space and pat yourself smugly on the shoulder thinking how well written these frameworks are. Tough luck. Even if you configure the heap size to something absurdly low like 32MB your JVM process will still use 120MB. Why? Because the JVM has a minimum footprint. It has lots of parked GC threads in the background that need 2MB stacks. It has to keep the class files in memory, keep profiling data, the generated code and lots of other things. If I need something that needs to consume as little memory as possible I just stick with C++ or Lua. With those two languages staying under 1 - 2 MB of RAM is trivial.

Also... it is possible your coworkers do not even set a maximum heap and just let it stay on the default values. That's how a tiny little CRUD app as a dashboard ended up allocating 10GB of RAM to itself on a server with 128GB RAM.

[0] well performing = low memory usage in this case


> Once the JVM has allocated heap space it will never let go of it.

While this used to be true in the past, some of the GCs available to the JVM may now uncommit heap memory. This is for sure the case with ZGC [1], G1 [2] and I think also with Shenandoah [3]

[1] https://openjdk.java.net/jeps/351 [2] https://openjdk.java.net/jeps/346 [3] https://shipilev.net/jvm/anatomy-quarks/21-heap-uncommit/


It is possible to write well-performing Java code. I’ve done it, and know others that have.

From looking at multiple projects over many years (as both languages have improved), my rule of thumb is that writing performant Java is about 10x harder than writing memory-safe C++.

One particularly insidious, and related problem is that getting the first working Java program is slightly faster than doing it in C++. There are people that have built a career by producing a series of 90% Java solutions, and rapidly moving on.

They leave a wake of human suffering behind them in the form of large teams that perform heroic efforts in order to clean up the mess. This certainly wastes many billions of dollars a year.


> my rule of thumb is that writing performant Java is about 10x harder than writing memory-safe C++.

This is a bold claim.

The JVM is incredibly good at generating fast code, to the point that even naively written Java is guaranteed to run in the ballpark of C/C++.

Writing memory safe C++... good luck with that one, especially as soon as you start using external libraries where memory ownership becomes very blurred.


> One especially annoying thing is that the JVM will not garbage collect until the heap is actually full.

Factually incorrect. Depends on the settings of the GC.


It does not need 840 MB of memory — it uses it because the purpose of this example is to show that packaging is now easy, and non-essential parts were left out to keep it simple. I just tried limiting it to 50 MB and it runs just fine.


I'm sorry to say this, but I think you made the wrong call here by treating startup time and memory usage as "non-essential". At least when presenting the case here, on HN.

Programmers here tend to be experienced in many different languages and platforms, and when we see a post about packaging an app, we stop and ask what is it that gets packaged first. In other words, what's the point of easy packaging, if the packaged result is slow as molasses and eats RAM like there's no tomorrow. We have literally dozens of tools at our disposal which make packaging as simple, yet the result would runs circles around your demo, even if - and here's the kicker - we considered performance and memory optimization as non-essential. Sane defaults and all that...

To sum it up: we're just as interested in how you package it as in what is in the package. If it's really easy to optimize for startup time and memory footprint as you say, then all the more reason to apply these to the demo. No one here would think the demo is too complex (not simple) just because of that. And if it's not easy... well, that's also something we'd like to know.


If I put -Xmx50m in the JavaOptions section of /opt/cljfx-hn/lib/app/cljfx-hn.cfg it still uses 650MB. How do you go about limiting it to 50MB?


It doesn't really need 840mb. If you force a GC after waiting for it to load the stories it needs "only" 350mb. Try "jcmd main GC.run" (if you have any JDK 11+ installed).

The problem is Java sees lots of memory lying unused and figures ... why should I work harder to free it up? It'll waste CPU time and energy and I might need the memory later.

In theory it actually will do background GCs and free memory back to the OS anyway so I'm not sure why it's not happening here; possibly that feature needs to be explicitly activated or maybe I just didn't wait long enough to see it happen.

As for why it needs 350mb of RAM, let's take a quick look:

    $ jcmd main GC.heap_info                                                                                                                  
    Tue Mar 31 10:53:42 2020
    61074:
     garbage-first heap   total 102400K, used 28139K [0x0000000700000000, 0x0000000800000000)
      region size 1024K, 0 young (0K), 0 survivors (0K)
     Metaspace       used 58206K, capacity 65566K, committed 65868K, reserved 1101824K
      class space    used 10342K, capacity 12810K, committed 12928K, reserved 1048576K
So as a first approximation, even after a GC and subsequent heap shrink, it's holding onto about 3x what it really needs. Total heap memory is only 28mb. That's a bit odd. Perhaps G1 is still not aggressive enough in releasing memory back to the OS. For desktop apps it'd really be better to aggressively let go of heap as fast as possible (NB: native apps don't always do this either).

The "metaspace" is holding a lot of stuff, mostly VM internal data structures. But I thought metaspace was primarily class metadata, but here there's only 10mb of that, so what's filling it up?

There's "jcmd main VM.metaspace" which tells us about 8mb of it is wasted due to fragmentation (I think). It doesn't tell us much about what's going on though.

These numbers look extremely high for a normal Java app. If I recall correctly Clojure [ab]uses the JVM in odd ways, for instance, by creating a class for every single function in the program instead of mapping them to methods as you would expect. This can easily cause a lot of overhead in class metadata compared to a normal program.

However the bulk of the memory usage you see is really just an "optimisation" - it's there, so why not be lazy and leave garbage lying around until you need the space.


> Firefox with more than 100 tabs opend consumes 1.2 GB,

Really? On my machine it eats 2gb for 10 tabs.


Yes, well, if they're just "opened" and not fully "loaded", then maybe it can get away with 1.2Gb. ~100 fully loaded tabs, which happens quite often with various online docs, take 24Gb RAM last time I checked. So your figure (2gb for 10) looks quite right to me.

Well, it was worse with Chrome, though not by much: the main memory savings come from Multi-Account Containers, which is a really neat feature, but otherwise they seem almost tied.


Just saying 100 tabs = 1.2 GB or 10 tabs = 2 GB doesn't make much sense as it depends on a lot more things than just the count of the tabs.

You'll need to also outline what pages are in those tabs, how much each tab consumes, what extensions you have installed, how your config looks, what your OS is, how much history you have, what is your usage pattern, what experimental flags have you/the system activated and more.

It just depends on so many variables. 10 tabs of CNN is gonna use way more memory than 100 tabs of Hacker News comments, as a basic example. But you have a content script that is injected on every HN page with live feeds of something, now 1 HN tab might use more memory than 10 tabs of CNN.


Clojure.


Case clojed.


Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something. https://news.ycombinator.com/newsguidelines.html

I explained the situation in a reply to grandparent comment if you are interested to know why it is like that.


>Please don't post shallow dismissals, especially of other people's work.

Nothing's been dismissed. The post is about a throw away program in a nascent React-on-JVM environment. People want to know why it uses so much memory. It's Clojure. Clojure is a memory hog. Case clojed.

Please don't be disheartened. It's a nice show and tell and resulting discussion.


I wonder if graalvm can be applied to the output of proton native which allows you to use ClojureScript to compile react code into QT but still in the context of JavaScript if graalvm can get a native binary out of that it'd be cool

https://gist.github.com/polymeris/0112389243890709108c4b8b0a... https://github.com/roman01la/proton-native-cljs


Very interesting to use a redux (re-frame) like programming model for building native desktop UI's. I have done some basic stuff with it and it sure seems powerful.

For anyone interested there will be a virtual meetup 16:th of April 6PM CEST: https://www.meetup.com/sthlm-clj/events/269204900


If you're interested in desktop alternatives for clojure, check out https://github.com/phronmophobic/membrane. It's different from javafx in that all the event handling code is pure clojure. For graphics, it uses skia, https://skia.org/.


Love the example with the spacer, guess we're back to building UIs with tables soon as well!

  (defn counter [num]
    (horizontal-layout
     (on :mouse-down (fn [[mouse-x mouse-y]]
                       (swap! counter-state inc)
                       nil)
         (button "more!"))
     (spacer 5 0)
     (label num (ui/font nil 19))))

Otherwise, haven't heard about Skia before, so thanks for sharing that, looks really nifty.


Thanks! More complicated layout strategies are easy to integrate, but spacer works really well for examples since most people can intuit what the result will look like.

Skia has worked well so far. Since it's used by Chrome, Android, and Firefox under the hood, it means that I can reach a lot of platforms "for free".


Not really native - it's JavaFX.

I love clojure but personally flutter seems way more viable for the use cases I could imagine using this.


As someone who used to like JavaFX and maintain a few desktop apps that use JavaFX, I have to say you're totally right.

Google seems to be putting huge amounts of effort on Flutter, and it can already run on the desktop (still early stages and in alpha I believe)... JavaFX is mostly orphan right now. Oracle seems to have no interest in it... so it seems to be currently being mostly advanced by Gluon[1], which honestly, does not have the muscle to pull out something competitive with Flutter or Electron.

JavaFX still has fundamental pieces missing... like setting an icon for the app or opening some URL in the system browser[2] (without using horrible hacks, which is what I've been doing - which get you lots of warnings since the module system was introduced, and may stop working soon). Not to mention no support for auto-updates, the fact that jpackage is still in "incubator" stage[3] and intentionally does not have JavaFX-specific features at all (it's just barely usable with JavaFX), the SceneBuilder, which could be such a great tool to develop UIs, similar to Lazarus, but it's nowhere as good, being in about the same unfinished state it was in around 2014, terrible documentation and lack of adoption (most examples you find online are from circa 2015, when JavaFX got a little traction)...

These things all make JavaFX a very hard sell for non-developers apps (if you're a developer who does not mind installing the appropriate JVM version to run the app, JavaFX works quite well - a 1MB jar can give you A LOT of features - one of my apps, my favourite and really useful, is only a 340KB single jar and works on any JVM 8+ - and looks as modern as any Electron app).

It's sad but the best way to get professional apps developed and distributed nowadays seems to be, by far, Electron (because of JS, and its stupid browser-based architecture)... but I am hoping Flutter will break Electron's hegemony soon as I have great experience developing Flutter apps on mobile and the web... Just try the online pad[4] to get a taste of what it looks like! It's amazing.

[1] https://gluonhq.com/products/javafx/ [2] https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8091107 [3] https://openjdk.java.net/jeps/343 [4] https://dartpad.dartlang.org/ecb28c29c646b7f38139b1e7f44129b...


> like setting an icon for the app

As in stage.getIcons().add()? Or do you mean for the binary that jpackager produces? I wrap my apps with launch4j which allows that. Then I bundle them with jlink-made JRE (about 60 MB). Works pretty well, total size is ~100MB for the whole app with all the dependencies, (not a Hello World app by any means).

>opening some URL in the system browser

As in getHostServices().showDocument("https://google.com")? Then again, nothing's stopping you from using awt's Desktop class, not sure I understand the problem here.


> stage.getIcons().add()

Doesn't work and never did on MacOS.

EDIT: bug (closed as won't fix!!): https://bugs.openjdk.java.net/browse/JDK-8095033

> opening some URL in the system browser

HostServices also does not work in all but Oracle's distribution of JDK 8, which is abandoned now: https://github.com/javafxports/openjdk-jfx/issues/540


> Doesn't work and never did on MacOS.

Gotcha, I work only with Linux and Windows. There was a problem where the icon was incredibly pixelated on Windows 7, but they fixed that.

> HostServices also does not work in all but Oracle's distribution of JDK 8

I see, I tested it only on Bellsoft's JDK 11, since I don't use 8 anymore.


JavaFX seems like it was a good incremental step over Swing but right at a time when the web’s paradigm (the DOM, a brilliant concept) was becoming popular and was a leap beyond Swing. So JavaFX has the same problem as Windows Vista: too little too late. If we had something like the DOM as a new UI kit, it could be a big hit if done right.


I'm not sure I see any real difference between the DOM and the tree of widgets you can build in Swing. What is it that's so brilliant about the DOM?


The DOM stitches several useful but unrelated concepts together. Besides just being a tree of composable views:

* You can query elements arbitrarily deep using different patterns (depth, class names, ids, tag names, attribute presence/values).

* You can attach event handlers to arbitrarily deep nodes that you just queried, not just the nodes at the level your "controller" is in charge of creating/updating like traditional frameworks.

* Customizing style and behavior is entirely done via properties and composition rather than subclassing like traditional frameworks.

* And more!


In that regard I think React Native has much better future than Dart/Flutter, specially with Microsoft backing it now, unless Fuchsia actually gets released.


Why is that? You mean that you're worried about Google calling quits on Flutter sometime soon?


Flutter is all about being the last opportunity to sell Dart, and I doubt that Chrome (PWAs) and Android (JetPack Composer) teams are that happy about its existence, given the political answers when questioned about Flutter on their own Q&A sessions.


I don't think purely about Dart, originally it seemed to be aimed as a technically more robust cross-platform competitor to React Native. Its implementation has good merit in that regard.

That said, it may well not survive nearly as long as RN will, time will tell.


How does TornadoFX compare? it seems more focused on enterprise apps though


TornadoFX is just a Kotlin wrapper around JavaFX. It makes writing apps much nicer, but has considerable cost in terms of even more memory usage and, obviously, due to the Kotlin runtime and lots of Kotlin wrappers for most JavaFX classes, app size.


Please note that the Gluon Client Plugin can be used to create native JavaFX desktop apps which take far less memory.

https://docs.gluonhq.com/client

When Clojure resolves all outstanding Graal issues, one could use Clojure for native apps too, but there are several bug tickets open on Clojure wrt Graal.


I wonder if anyone has tried cljfx->Android with Gluon's stuff


Last time I tried, cljfx (actually JavaFX in general) doesn't play too nice with GraalVM, I'm not sure Gluon actually addresses that, so it'll be hard to add another layer (Android) when the basic layer doesn't work.


is it part of the same pipeline? Graal is compiling to native while Gluon on Android I assume is running JFX on Dalvik instead of JVM (it possible I don't get how Gluon gets JFX to run on Android)


> browser-based technology struggles to utilize multiple cores in the same VM instance

> javascript VMs choke on processing large amounts of data, which leaves them a better fit for advanced interactive forms than compilers or data processing pipelines

2005 called and wants it’s quotes back. It is trivial to spawn worker processes today and VM performance is only 2-5x behind compiled ones, often closer. Not to mention WASM.

The elephant in the room is not JavaScript or it’s VM but the browser engine.

EDIT: seems like JavaFX has its own browser-like “scene graph” and embeds WebKit, so this will suffer from the same bloat in memory use and app sizes?


The real question is why though. Why should we use the HTTP/HTML/JS/CSS universe to deliver interactive applications to users. I only see downsides here. Not to mention that we pull these things into native applications like it was great to begin with.

WASM might be able to address some of the shortcomings of this stack, not sure.


Because it is an extremely effective model for building UIs. As I mentioned above even this project uses a similar architecture, only backed by Clojure. Any UI toolkit will have similarities with the web stack.

The real issue is the size of these monster engines, but a lightweight engine that only supports the subset needed for apps is possible - see Sciter, and I have high hopes for https://ultralig.ht/


>>Because it is an extremely effective model for building UIs.

Is it? Have you ever tried? I think it is extremely hard to build reponsive UIs that work on every platform and there are no UX problems. We have to deal with a giant amount of boilerplate in many cases because using vanilla things (JS especially) won't cut it anymore. I am not even sure how many frameworks are targeting this space and try to solve the typical issues with these technologies and fail miserably. I think some of these issues are finally started to be handled the right way (Elm or ReasonML) but there are still some painpoints left (CSS). The question still, is it worth it? Couldn't we do better? The performance implications (energy consumption) for an average website is just hilarious. We should have a day where everybody disables JS and measure how much percentage of our computing capacity is going to render websites because we do not want to move away from HTML which was not (by any strectch of imagination) designed for this to start with. If anything, this should be a concern to most of us.


> Have you ever tried?

I’ve been doing it for 15 years.

> I think it is extremely hard to build reponsive UIs that work on every platform and there are no UX problems

It is hard on native platforms as well. Developing for mobile is not that far off from web today, everything converging towards flexbox or constraint solving, reducers etc.

I think you’re conflating the problem of a bloated JS stack with the platform itself. We were building perfectly fine interactive UI without any of this crap ten years ago, and can still do it.


HTTP and HTML were not designed to do this. It was specifically static and stateless. We are retrofitting statefulness and dynamic content to this platform. Imagine if we designed a stateful and dynamic platform from the ground up that could be implemented with minimal effort to every platform and started to use the right abstractions with the right set of tools.


WASM doesn't help at all here as it wants to be a new instruction set architecture (without even DOM access and GC) when what's needed is a portable and compact UI API.


“Processing large amounts of data” is one of the reasons in the page


> seems like JavaFX has its own browser-like “scene graph” and embeds WebKit...

Since Java 9 modules were introduced, the webview (and webkit) are only included in the app if the `javafx.web` module is used. Otherwise, it will not be included in your distribution if you pack it with jpackage.


This looks nice and promising for multi core apps. But, 85Mb for a demo app? It sounds heavy and might end up like intelliJ IDEs I think. How about compiling to binaries with Graal native? Can it be done?


Yes, it can be done: JavaFX developers created a library that deals with various pitfalls of compiling it to GraalVM: https://github.com/gluonhq/substrate

I think using jpackage is easier to start with since there are less cutting-edge moving parts involved, and it imposes fewer restrictions on the runtime.


I hope this one picks off quickly! Electron needs serious competition


I justed checked the RAM/CPU usage on my windows machine. Its 580Mb/5% constantly!! This is way higher than electron tbh! :(


Most interesting takeaway for me was jpackage working with GH actions. V useful. Who needs a local build system now? ;)


Same here. Have been trying to build something similar with GitHub Actions and this is a nice reference.


This is going to sound subjective, but: you can't claim that something is a good desktop-app building experience in 2020 and lead with something that looks like... that.

Browsers have simply set the bar too high.


Why do you package a javascript VM at all? You could have a local server written in clojure listening on localhost:<someport>. The entire js-driven UI could just be in a browser tab.


One issue with that is the lack of encryption and/or security between UI and the local server. Also I always forget the port for the local host style apps. If PWA's were easier on desktops that'd help.


> outperforms plain React a lot of the time due to optimizations that re-render components only when their inputs change

Is this not exactly how React also works?


Not quite, because if react is re-rendering a component it re-renders all its subcomponents as well whether they've changed or not - the DOM diffing stuff means it won't screw with the DOM for stuff that hasn't changed, but it still has to regenerate it to find out.


So reagent skips this by only re rendering a parent of only the parent changed and only re rendering the child of the child’s j out changed? (Presumably using its atoms for props then too and not just state?) This somehow sounds error prone.


That sort of thing is honestly pretty pleasant to write and test in lisp like languages.

I wouldn't want to try and write it in javascript, but I'm entirely willing to believe that clojure/cljs got it right.


I don't see what s-expressions have to do with this concept. It should be just as doable and nice in JS...


Anyone knows how Jpackages compares to good/bad old JNLP or is that complementary?


I've just started designing something very similar in my free time, but for Go (golang): interface around native GUI + WebKit webview + React + bindings.

I'm glad that we are slipping away from Electron...


Have you checked React Native for Windows and macOS?

It goes even further as it get rid of the webview

https://microsoft.github.io/react-native-windows/

Native widgets, bridge between JS thread and the native world. That gives you good performances and the flexibility of JS/Typescrit and React to glue things together.

(I haven’t tried yet, but that looks quite nice on paper)


Of course I have, but that's not exactly what I was aiming for... ;)


Hm, as far as I know, the problem most people have with Electron is not around the language, React or bindings but the fact that there is a embedded browser in there that adds a lot to the size and memory consumption (and security issues).

I'm curious to hear why you're glad we're going away from Electron into something that seems to be about the same thing, albeit a different language?


I expect it to be a little better trade-off given the following points:

1. JS is kept for what it's meant to be and do; nothing more.

2. A WebKit webview is probably lighter than Chromium + NodeJS.

3. The whole thing should be managed to be linked as a shared library, avoiding many pitfalls of Electron's applications (by means of a semantic-versioned library).

4. The whole solution is really language-agnostic, despite my efforts with Go.


[flagged]


The “problem of concurrency” isn’t about solving embarrassingly-parallel problems by spawning async threads and letting them run. Every runtime can indeed do that, but that isn’t worth mentioning when talking about “concurrency”, because you can also do that by just spawning multiple of your runtime itself. (I.e. if you’re not doing any IPC of note, then there’s no difference between multithreading and multiprocessing. In the web case: you could just use an iframe.)

The “problem of concurrency” is really the problem of having cheap IPC between your threads (i.e. the ability to pass around large amounts of data using small messages/handles), so that you can shape your threads into architectures like a staged data-flow pipeline, or a set of schedulers for an actor system.

JS does not have cheap concurrency. (What would cheap concurrency in JS look like? Well, the ability to make an ArrayBuffer immutable, and then pass the immutable ArrayBuffer to another Worker Thread “by reference”, would be a good start. Add “mutably-immutable by copy-on-write” ArrayBuffers on top, and you could actually afford to pass ‘ownership’ of an ArrayBuffer back and forth, writing to it across multiple threads. Then you’d have something that you could maybe run a multithreaded WASM program on top of and have it run to completion before the heat-death of the universe.)


You have some serious prejudiced opinion about how to effectively do work division to achieve either for concurrency or parallelism.

My only suggestion read some concurrency patterns and most importantly try to implement them using modern JS features before making such statements.

May be you can find this link as starting point - https://benchmarksgame-team.pages.debian.net/benchmarksgame/...


What I'm talking about is how to effectively saturate multiple CPU cores in mixed IO+CPU-bound workloads, e.g. compiling/parsing, OLAP data-analysis/reporting workloads, etc.

Javascript's primitives get you 1. the ability to cooperatively multitask within each thread, and 2. the ability to copy data between threads, but this is, in the end, just what you get from a pure NUMA (e.g. GPGPU, DSP) architecture.

There is a reason that multicore UMA CPUs exist; there is a (very large) niche of workloads they specialize in. JS cannot currently saturate CPUs in such workloads.

See, in your own link, the n-body and regex-redux benchmarks. These are examples of workloads that actually take advantage of the kind of memory-sharing / "message passing carrying reference handles" IPC that multicore UMA CPU architectures are designed to accelerate. Note V8's core saturation on these workloads.

The kind of workloads that can currently be parallelized in Node, are the exact kind of workloads that are better run off-CPU anyway, in cards like GPUs or FPGAs dedicated to the niche of pure-NUMA workloads.


I shared the link to look at all examples and yes its not the end of the world, it's starting point. But like i said you are prejudiced to only look at where NodeJS lags behind.

NEITHER you understand the simple fact that its not required to pass messages every now and then between master and worker threads, it can be done in batches NOR you care to look at JS and find there is something like SharedArrayBuffer.

I will say again, please don't guess, write the code for real life use case, invest some time learning new JS API and only then compare benchmarks.


Your comments comparing worker threads to what can be achieved on the jvm (or natively) with multithreading is just a sad display of ignorance. Even more sad is the tone you used.


It's a matter of perception and real life experience.

I have spent more 13 years writing concurrent/parallel multithreaded code on JVM and 3 years on NodeJS/GraalJS as my job requires it.

About the tone, First, i have found the overall tone of the blog post very rude. Author seems to have no experience with doing JS and using its feature. Lie does not become a truth if presented in sugar coating. He is just fanboy of Clojure. Second, You are saying i am ignorant without giving either an example or sufficient explanation. who is more ignorant ?


If you have 13 years experience writing multithreaded code on the jvm and your answer to a statement about js vms struggling crunching data is: WebWorkers, my friend, you just don’t have a clue.


GruntJS/GulpJS/Webpack - none of these address the issue of transitioning to new behavior without having to fiddle with current state / fix existing object / reset and recreate some things from scratch. Clojure addresses that by forcing immutable functional mindset.

Sure, there’s a combination of tools / plugins / unorthodox (to vanilla JS) development practices that gives you the same thing, but in Clojure you just get it out of the box and nothing feels weird.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: