NeutralinoJS: Lightweight Electron alternative using native browser controls
neutralino.js.org
neutralino.js.org
Chromium is built to share a lot of things between tabs efficiently, but this economy-of-scale doesn’t translate over into having multiple instances of Chromium running. Each instance gets its own GPU renderer process + font-glyph tile cache, its own network-request cache (i.e. its own in-memory key-value store with its own LRU limit), etc. Running multiple Chromium instances at once is a bit like running multiple copies of an RDBMS on the same machine and expecting them to not fight over memory.
Does anyone know what exactly this “base” overhead in Chromium consists of; and, more importantly, how often it changes relative to Chromium releases? Because I’m wondering whether it’d be possible to factor most of it out into a separate layer from the highly-iterated-on “browser engine” DLL stuff (the renderer + DOM + JS layer) into its own sort of “Chromium Core Services” DLL, which could be relatively-more ABI-stable.
If you had that factoring-out, you could achieve a pretty good memory + CPU savings just by having Chromium + application frameworks like Electron all share one copy of the Chromium Core Services between them, while keeping their own higher-level “renderer+VM” DLL on top (which would hopefully be pretty stateless in terms of shared state, only adding the ~50MB mmap(2) overhead of the DLL-image itself, and then whatever memory the individual render-contexts consume.)
Alternately, of course, you could build Electron as a pseudo-browser where Electron “apps” are just separate windows running in its memory space, and the native Node processes are all just distinct V8 Execution Contexts in the same process. But 1. this would lose you the ability to develop for a particular version of the renderer, which is what Electron gains you over projects like Neutralino in the first place; and 2. this wouldn’t give you the benefit of Electron sharing service-memory with Chrome itself.
[0]: https://lifehacker.com/why-chrome-uses-so-much-freaking-ram-...
[1]: https://stackoverflow.com/questions/53658769/why-vscode-requ...
I have a copy of Slack running that’s using 2GB right now, with no window open.
The solution is for operating systems to inject deliberate back pressure on memory allocations for user-selectable applications to stop them from behaving this way. "No memory for you. Try mallocing smaller chunk. Maybe I grant; maybe I don't. Maybe I just kill -9 you if you bug me too much." This is exactly how I wish my OS would treat web browsers.
Spotify: 1083.7 MB. VS Code: 890.9 MB. Skype: 406 MB.
In the past, I’ve had projects open in VS Code which have caused that number to shoot up into the multi-gigabyte range.
Of course this is all anecdotal but I am pretty sure these numbers come from actual experiences that people are having with Electron apps.
~60mb memory usage at startup.
Right, I'm convinced.
Try this: open a new Chrome instance; look at its memory usage; then load a bunch of tabs (try an Open All on a bookmarks folder), close them all again, and then look at Chrome's memory usage again. It will have increased.
Chrome isn't leaking memory; instead, it is doing the same thing that an Operating System does: weakly reserving memory to optimistically cache stuff, releasing it again if there's enough memory pressure.
This works okay if you only have Chrome itself running. (Even then, its cache fights a bit with the OS page cache. It's certainly no Postgres, intentionally getting the OS page-cache to do its caching for it.) But as soon as you have two separate Chromium instances running (e.g. Chrome itself, plus an Electron app), then each one is going to try to "optimistically" cache as much stuff as it can, until it runs into the memory pressure created by the other instance caching as much stuff as it can, and so each instance will unload just a bit to let the other one cache just a bit more—back and forth—forever. Together, they thrash memory, and it becomes much harder for them to actually accomplish the "weak" part of "weak reservation", instead ending up hoarding all the memory between them.
This is the pretty much the same problem you see if you try to e.g. run two memory-heavy JVM processes (e.g. ElasticSearch) on the same system. Chrome is just one of the few times you'll see this "recapitulation of memory management within the runtime" effect client-side, rather than server-side.
(Regarding uptime, it's not at all uncommon for Chrome's memory usage to double or even triple overnight. Partially a function of the websites you have open, sure, but also partially a function of Chrome itself).
It is difficult for me to lay blame at the feet of application developers (assuming they didn’t make the choice to use Electron) when the platform itself is so bad at giving developers ways to manage their apps’ memory usage.
If you want to listen for an event on a global object like `window`, the platform could give a way to do this using a weak reference, but it doesn’t, so you have to make sure to always remember to manually remove the listener yourself or else you’ve just leaked a bunch of stuff.
If you want to load an image, or some other media, the platform could give a way to control the runtime’s internal caches so you aren’t retaining data in memory that you don’t need—but it doesn’t, so you have to hope that the generic runtime memory cache from Chromium is intelligent enough not to retain unimportant things (and, in my experience, it’s not).
This problem has existed in one form or another in every Electron app I’ve ever used. Some are certainly worse than others, but I don’t think I’ve ever seen one come within even an order of magnitude of the memory usage of similar apps written natively.
Desktop Spotify has been a resource hungry mess for years now. It used to be known for prematurely wearing out SSDs by constantly writing stuff (cache I think?) to disk.
They were constantly vacuuming some SQLite database.[0]
[0] https://arstechnica.com/information-technology/2016/11/for-f...
On macOS, this will start a ReportCrash process, and end up taking up an entire CPU core just repeatedly crashing, reporting a crash, restarting, etc. It also significantly impacts battery life.
This happens every day for me. It's arguably worse than the SSD-killing SQLite vacuum problem they had.
Weirdly enough, the crashes don't seem to affect any of the app's functionality.
This is absolutely dreadful. I've unfortunately worked in multiple organisations where teams deconstructed their web-based products into microservices for this very reason.
This is just insane.
I've come up with a powershell snippet to fully quit keybase and reclaim ~1GB RAM:
get-process | Where-Object {$_.path -match "keybase"} | Stop-Process
Me too, and you just made me realize, there already exists a system for running Electron apps in a shared instance of Chrome, and it's installed on almost everyone's computer! You just... open the web version, in Chrome :)
† I once designed a system that has Electron install an Erlang release to the client, register it as a service, and start it. The Erlang node runs a locally-bound web server; the Electron app then visits that web server. All the data-wrangling happens in Erlang land, with Electron just serving as an HTML renderer for the pages coming from the local server. And yet Electron's native side is essential, here, because it ultimately manages the Erlang release (installing it, updating it, etc.) Erlang doesn't have any good tooling for doing client-native stuff on its own; Erlang devs find the whole idea of deploying an Erlang release to a client machine funny.
That's ~230 MB. I can get it up to ~275 MB but it seems to return to normal pretty quickly.
gps | ? path -m keybase | kill>If one chooses a chromium channel in the launch options, then carlo calls puppeteer createBrowserFetcher which will download the chromium revision compatible with Puppeteer or even a specific revision. sunglasses
In regular ElectronJS you have the same Chromium bundled with the app. Not sure if puppeteer installs a local or system wide Chromium.
VSCode, say, as a slimmer download sans web runtime is a possible outcome.
Native desktop apps are never coming back. It will either be Electron or mobile apps emulated on desktops.
So, perhaps, soon we will have PWAs instead of bunch of electron apps each carrying its own Chromium.
That is how they work across ChromeOS, Android and Windows, with Apple being the outlier for obvious reasons.
I think there will always be a category of people who prefer using native apps for performance, security, or access to OS features. Anecdotally I looked at the apps I have installed and I found out that I don’t have many electron apps on my Mac beside chat apps and VS Code. I suspect non tech-savvy people might have even less, since they do a lot more inside their browsers compared to before.
I’m not sure what you refer to when you say “native desktop apps aren’t coming back”, given that they never left t begin with.
Now, as I finish up this response to you in Safari, a native desktop app, remind me what were you saying about native desktop apps again?
† I don’t know what to call this editing paradigm. It’s somewhere between source-editing and WYSIWYG. Document markup nodes are rendered as their source syntax when your cursor is “inside” them, allowing for plaintext modification of the syntax; but then, when your cursor leaves a node, it switches to rendering as its formatted output instead. You can cursor back into the node to edit it more at any time. It’s pretty unique. It’s the editing style that Slack’s new input box wishes it had.
> Native desktop apps are never coming back
I really hope you're wrong. This would be a terrible outcome for so many reasons. I refuse to use Electron apps on my personal laptop on account of the absolutely noticeable degradation of performance and battery life from using these Electron apps. Using vscode literally halved my battery life.
KDE3 did the same with KParts, but it was several times better.
It would be less efficient and more wasteful to continue things as normal where every program has its own Electron. People already use Electron type apps. New ones are being made every day. That's not going away. If Microsoft and Apple put this in the OS, it would decrease the user's resource utilization. It's a net win for everyone except maybe chip manufacturers.
As for desktop Linux, GNOME3 already works like Active Desktop did. The whole shell is JS (and the GObject mess down below). I have used a lot of desktop environments. I've been skeptical of the JS eating the world trend. But I have to admit it's smooth. I had bad experiences with KDE (Qt/C++). Not only does it look ugly, it segfaults constantly.
Would it have been better to make a desktop environment in something more, uhh, professional like Go or Java? Of course. But that's not what the crowd decided. There's nothing quite like React for native desktop GUIs. Regardless of your opinion of JS, you have to admit that the React way was a game changer. Since there are no alternatives, the path of least resistance is to simply use React as is for desktop applications, which is exactly what has happened.
I have to kill gnome-shell maybe once a week. That's a huge improvement over KDE.
Xfce to me is just too ugly to use. And it messes up my system with all these trash Xfce-only applications and mime associations (seriously what's exo-open and why is it still causing problems 2 years after I uninstalled xfce?).
Bloat is something only insiders care about. I care more about adoption of free software than I do about its quality in the academic sense. The way to get adoption is UX and looking nice. If you're trying to convince someone to switch from Windows, what would you show them, Xfce or GNOME3?
>GNOME3 is polished and works, Not even close. It uses huge loads of RAM while Budgie, while using the same technology, is far snappier. For a current user, I'd suggest Solus with Budgie.
They never went away to start with, it is just a couple of Electron outliers out there.
We've done this. The layer is called a web browser. You deliver runnable code to it using something called HTTP.
> 1. this would lose you the ability to develop for a particular version of the renderer
html and css are standards, if you are developing for a version, you are wrong. full stop.
> 2. this wouldn’t give you the benefit of Electron sharing service-memory with Chrome itself
there would be no electron so point is irrelevant
Who said anything about HTML and CSS?
Apps like VS Code create and manipulate a DOM directly in JS, or more often these days, WASM. To these apps, Electron is just a virtual machine with a lot of browser-like multimedia APIs attached. You pin the exact version of the renderer just like you pin the exact version of your dependencies in a server-side app, or the exact version of the OS in a virtual-appliance VM image.
Electron has nothing to do with "the web" or "web standards" other than borrowing technologies originally developed for those. Electron competes with things like Unity and https://love2d.org/, not with browsers. It's in a category of frameworks with the goal of giving the developer pixel-perfect control over what the user sees, where apps are separately developed, tested, and tweaked, for each target (e.g. each OS, each kind of display/interaction methodology, etc.)
With this category of framework, you don't build your app as generic code targeting a standard; rather, you build, and test, your product against a specific "engine." (Imagine for a moment that people could take Unity apps and run them against any old version of the Unity engine, or even against Amazon's Lumberyard fork of the same. Would anything work?)
in much the same that sharpened sticks compete with power tools, yes
Similarly, if your goal is drawing the views of a CRUD app, any tool you use is going to be used as a glorified version of Display Postscript.
There are, however, very stable in-browser ways of dealing with the question of client-side storage. IndexedDB is one. But there is another that I am quite partial to, called the File System API. I use it to locally store the files and code in the browser-based OS called Linux on the Web (https://dev.lotw.xyz/desk.os).
Analogy: why would you ship a new copy of X/Wayland just because your Desktop Environment has a new release? The new DE version uses the same old compositor, because the compositor doesn't change much. So why would it make sense for a compositor to be part of the DE and embedded in the release package for the DE, rather than just requesting the OS to make available to it the services of a compositor of a specific [ABI protocol] version? Even if the compositor and the DE have the same maintainers, it'd make far more sense for them to be two separate projects, with one just depending on the other.
Or, to put this another way: why should ChromeOS devices (= another build target of the Chromium codebase) replace the whole OS image every time some new namespaced CSS layout rule is being incubated? That stuff has nothing to do with running an OS. The parts of Chrome that are an OS—which includes a lot of the stuff that gets installed as "browser" on other OSes!—should really live as OS services, separate from "the browser" (really just the renderer + VM.)
Microsoft took a while but eventually learned their lesson about this, and these days Edge is just an app and updating it doesn't require a restart of your computer. Part of what enabled that was delineating the slower-moving parts of the browser (e.g. the network stack) from the faster-moving parts, and letting the slower-moving parts live in the OS while the faster-moving parts lived in the app. "Edge" is just a renderer+VM. So is Safari, for that matter—what you download when you download "Safari Technology Preview" on macOS is just a renderer+VM, which relies on the same OS-provided libraries for e.g. network caching that the system Safari installation does.
yey, currently using 6.6 gigabytes on my machine (RSS)... efficient...
firefox is no better, sitting at 4.8 gigabytes...
Only happens with electron apps.
A simpler approach may be to just publish a basic web server as your app (express on top of node would do) that runs on the local machine on some random port and have users just go to that URL with their regular installed browser. Bonus points for no leakage of security / CORS / etc. issues from the UI side of things, every unsafe thing needs to be node-side where it can be better controlled. Also enforces asynchronous and efficient communication between the UI and the "local backend".
The "back end" web server can be running on whatever language stack you like, be it Go, Rust, Haskell, Common Lisp or whatever. Syncthing is one example, where it's nominally headless, but there is a local web server that can be pulled up to set preferences, monitor status and so on. Works great.
I'm the author of secure-electron-template, a template with secure practices baked-in. Security is very important and I don't feel people think of that from the get-go.
When developing locally I presume it would be very similar to an Electron project, with npm dependencies for express / fastify on the "backend" side and React or whatever you need for the "frontend" side.
During build, you'd produce 2 bundles (frontend and backend) that tree-shake, etc. such that all js dependencies are optimized together. The frontend bundle should load in any browser (it'd be just a normal website with the only difference being that all REST calls go to /localhost). The backend bundle + any native modules will be loaded in the embedded node runtime so they can be distributed as loose files or packaged in something like Electron's asar.
There, I designed the whole thing, now someone go build it :)
This is Windows after all.
[1] https://docs.microsoft.com/en-us/microsoft-edge/hosting/webv...
This alone is a major reason to use Carlo[1] instead, which is arguably more secure (by virtue of not using IE) and produces even more lightweight bundles than NeutralinoJS.
(I was part of the team that launched it.)
That said, it is unmaintained and I don't expect that to change.
Well that's kind of a non-starter. Part of the appeal with shipping an entire browser in an Electron app is to give yourself a fixed target.
It always seems that these "Electron killers" all have some fatal flaw, be it "just use whatever the system browser is" or "just as good as Electron, if you don't care about file drag-and-drop, system clipboard integration, full CSS support, minor things like that"[1].
./neutralino-mac
dyld: lazy symbol binding failed: Symbol not found: __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
Referenced from: /Users/rcarmo/Downloads/New Items/neutralinojs-v1.3.0/./neutralino-mac (which was built for Mac OS X 10.15)
Expected in: /usr/lib/libc++.1.dylib
dyld: Symbol not found: __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
Referenced from: /Users/rcarmo/Downloads/New Items/neutralinojs-v1.3.0/./neutralino-mac (which was built for Mac OS X 10.15)
Expected in: /usr/lib/libc++.1.dylib
zsh: abort ./neutralino-macNo need to push a full browser engine when the OS already has enough of them available.
Welcome to the corporate world.
Not all of us. I'm happy to lose the 2.16% [1] in return for saving a lot in time and effort.
(1) https://developer.microsoft.com/en-us/windows/pwa/ (2) https://www.pwabuilder.com/
The problem with Electron apps is not so much Electron or Chromium but how they are designed & developed. Most developers view memory optimization as an after-thought. This is especially true for multiple document interface applications. The tooling is all there to monitor and optimize memory, but few make real memory budgets part of their build & integration testing process.
Developers have 16-32 GB of memory (or more) workstations and typically work on individual features that don’t exercise the edge cases of large datasets. They don’t bother to implement proper LRU mechanisms, paging, and often do performance optimizations that sacrifice memory for speed.
It gets worse if they aren’t forced to dog food their own creations or aren’t users of their app themselves.
The reason I wouldn't want Electron to be shared across apps is I don't want a system wide install and certainly don't want to deal runtime version type issues. Electron would start having to be backward compatible for years.
That's why I really try to get into docker for instance, sure good old unix sysadmin and configure scripts might arguably be more elegant and efficient but it's a huge pain to maintain and can (and eventually will) break out the blue after a system update because of a regression in some obscure library. Meanwhile docker images should work mostly everywhere without any effort and you only update your dependencies when you want to update them.
So now that storage is cheap and RAM is plentiful I think shipping the dependencies as part of the executable is a reasonable approach most of the time. The problem with electron is that the dependencies involve a full web browser and half a trillion libraries. That's the insane part. I have full python virtual envs for non-trivial apps that are a fraction of the footprint of an electron hello world. It's the core technology that's broken, not the packaging.
This is actually already how the Microsoft Visual Studio C Runtime (aka MSVCRT) is distributed: each application embeds or streaming-downloads the exact [ABI] version of the runtime it needs, but only one of each exact [ABI] version of the runtime needs to exist per system, and each of those runtimes can be upgraded by the OS to resolve security problems (while never altering the ABI guarantees that version distribution makes.)
As such, a runtime built this way is a lot like a base layer in a Docker image: it’s a static dependency of the app, installed during the app’s installation, and held in a pseudo content-addressable store that deduplicates it from other apps’ demands for the same dependency.
Except when they don't. Took me way too long to accidentally install the right redist to have fn-key OSD (volume up, mute, airplane mode, et cetera) on my ThinkPad.
Because why bundle it with the drivers? No error messages either. Just mash the buttons and have no visual feedback.
It's the modern "It works on my machine", but in this case it does because you almost ship the entire machine.
Knowing exactly what browser version you're developing for gives you a lot of freedom that you'd lack when having to support different ones.
The Javascript side calls the system via the local network, passing through this router: https://github.com/neutralinojs/neutralinojs/blob/master/cor...
I hope that there is some form of authentication to make sure no random web apps are accessing the API.
What they might actually do is to locally launch a HTTP server which renders the app if you call it on any browser (localhost:8080 in the examples). The chrome of the app is actually a thin layer that use the default OS web browser. That is why the memory footprint is much less higher that alternatives like electron.
I see mentions of WKWebView(the system webkit) in the code[0], and while I don't know any Windows programming, they are #including <mshtml.h>[1] - looks like a system included web view (presumably the one that edge was based on). The linux build requires libwebkit2gtk-4.0-dev to be installed[2] - they're dynamically linked to webkit2gtk.
[0] https://github.com/neutralinojs/neutralinojs/blob/b10e065c97...
[1] https://github.com/neutralinojs/neutralinojs/blob/07fdc3c1c7...
[2] https://github.com/neutralinojs/neutralinojs/blob/master/REA...
Developing an app requires node, but it will not be needed at runtime if I get this right.
The perf overhead (memory, bundle size) is big, but it's not stopping anyone from shipping Electron apps (Visual Studio Code, Slack, WhatsApp, etc..).
A nice to have compromise would be to have currently running Chromium Electron instances to host newly launched Electron apps, it doesn't solve the bundle size issue, but at least solves memory bloat and the number of Chrome main processes running.
https://github.com/neutralinojs/evaluation
For a moment I thought Neutralino didn't support macOS.
Neutralino uses MSHTML and WebKit so its already outdated, and it won't run on the web. So why would I use it?
What does this mean?
It’s a trade off: it’s much more lightweight but loses cross platform consistency. For some that might be absolutely fine, for others it may not.
If that isn't desired, then doing native apps is the way.
But a) it's a commercial product and b) uses it's own scripting language instead of JavaScript. But from some initial noodling around with it the browser engine is very capable and the footprint small.
WHY do these projects insist on doing this? What is it about UI that leads people to think this is a good idea? Do not make me learn yet another language just to use your UI library.
It's not that FreePascal or Dart are bad languages. It's that they're another language to consume yet more precious attention and head space, and there's nothing compelling enough about them vs. C++, Rust, Go, Python, JS/TypeScript, etc. to make it worthwhile.
What do you mean by "write GUI"?
If to declare DOM structure then HTML is for that.
If to initialize DOM structure from code at runtime then this (C++ here):
use namespace sciter;
element root = window.get_root();
element div = element::create("div", L"Hello world");
root.append(div);
div.attach_event_handler(my_controller);
Or do you mean something else?Applications that use Sciter for their UI are native applications. Like Norton AntiVirus for example. Or any other app of these vendors: https://sciter.com/#customers
a) Is anyone brave here to finance transition of Sciter to OpenSource? Please contact me if yes.
b) Sciter's script is a JavaScript++:
It uses JS syntax and runtime like `arr.concat(a,b)`.
Grammar and syntax was extended to better support UI cases. Like `const width = 12px;` is valid construct as Sciter has Length data type.
React and JSX are implemented natively.
function render() {
return <p>{this.greeting}</p>;
}
is a valid construct as JSX (SSX in Sciter) is a part of script grammar : https://sciter.com/developers/sciter-docs/reactor-and-ssx/But I want to give you credit for an absolutely phenomenal project! If I had the cash lying around to finance it as an open source concern I would, but alas.
https://github.com/neutralinojs/evaluation/blob/master/READM...
This page was last updated 2/28/2019. Last month, Microsoft Edge was rereleased as a Chromium based browser. Presumably this would change the Windows frontend for Neutralino to Chromium, but I'm only speculating.
Edit: It appears that a Chromium system-provided web view for Windows is still in developer preview:
https://docs.microsoft.com/en-us/microsoft-edge/hosting/webv...
Source?
Even disregarding the poor term you've used, I'm pretty skeptical of what you just stated. Especially with a margin error of 1%.
> An uncompressed Neutralino app is only ~5MB and compressed app size is ~1MB.