The State of Desktop Applications in Node.js
Though Node.js is known for being a server-side platform, interest in using it for building desktop applications is growing rapidly. Bindings exist for GUI toolkits such as GTK+, QT and Cocoa. However, one of the advantages of using Node.js for web development is the ability to use the same language on both the server and client.
It's possible to use Node.js together with a web browser shell to develop cross-platform desktop applications–and games using WebGL–using HTML, CSS and JavaScript. Let's take a look at some of the most popular options available at the moment.
The Contenders
Chrome Applications
The most basic way of running a "desktop" application is to run a local server and use the web browser as the user interface. Chrome's command line exposes an extra flag to run itself in "application" mode, hiding everything but the web view.
One example of this is Morkdown, a cross-platform application to edit GitHub Flavoured Markdown. It starts a Node.js HTTP server locally on a random port, then launches Chrome using '--app=http://localhost:' + port
as a command-line flag.
There are a few downsides to this approach. To be able to use the application, the user will need to have both Node.js and Chrome (or Chromium) installed on their computer. Ideally, desktop applications should be self-contained, allowing the user to launch it and use it straight away without runtime pre-requisites.
Chrome applications don't feel entirely like desktop applications. Outside of the web view, operating-system-specific features and UI items can't be modified, and it isn't possible to brand the application (it will appear as another Chrome window). Since the application is running in Chrome, users still have access to regular web browser features, and can open new windows and tabs and even the Chrome Developer Tools.
The need to have a server running to access the Node.js APIs means having two parts to the application: a HTTP API or WebSocket interface for the browser to talk to, and a web frontend to communicate with the server. This results in an undesirable layer whereby you have to write the server and the client separately, without the ability to run Node.js within the frontend.
node-webkit
node-webkit is a web browser shell built on Chromium, allowing for the Node.js API to be used alongside the DOM API within the same context. As a basic example, you could replace the contents of body
with a file read via fs.readFile
in a script
tag like so:
](https://github.com/rvagg/morkdown
It's also possible to use modules from npm and require them the exact same way. Native addons are also supported to extend both node and node-webkit, however they must be built using nw-gyp. node-webkit comes with a library to manipulate external parts of the shell, including the menu bar, tray icons and clipboard.
node-webkit has been used to build many high-profile applications, including Popcorn Time, Light Table and Komanda.
node-webkit applications are configured via a window
key in package.json
, outlining various properties of the application such as the entry document, width and height amongst others.
node-webkit has a major advantage to Chrome applications, as both DOM manipulation and Node.js API calls can be used in the same context without needing to run a separate server. The only caveat to this is that modules pulled into the application via require
only have access to the Node.js context, not the web view's. This means that Node.js modules must exclusively use functions and classes provided by Node.js or modules from npm, as the DOM is off limits. To get around this, you can include JavaScript using script
tags.
Due to the nature of how node-webkit is built, it can take some time before new Chromium builds are pulled in. node-webkit modifies some of Chromium's code to integrate the Node.js event loop, and as such, a direct pull from upstream isn't feasible.
atom-shell
atom-shell–similarl to node-webkit–is a shell built using components from Chromium. It was designed for the Atom text editor developed by GitHub, with slightly different architectural goals. Rather than needing the entire Chromium source, atom-shell only builds libchromiumcontent. Building atom-shell is much faster than building node-webkit as a result. atom-shell uses an alternative method of integrating libuv with Chromium's event loop, as well as using an upcoming feature in Node.js 0.12, multi-context.
There haven't been many large applications built with atom-shell apart from Atom itself, however atom-shell is fully documented.
The distinction between the browser shell and its runtime allow for cleaner organisation of code that deals with application state management and the logic needed to power the UI, compared with node-webkit. Unlike node-webkit, application configuration is done via an entry script rather than an object in package.json
. As a result, atom-shell is more flexible than node-webkit for application state customisation at startup.
Unlike node-webkit, atom-shell uses APIs exposed by libchromiumcontent instead of modifying Chromium directly, allowing easier upgrading of the renderer. This results in new Blink features being brought into atom-shell at a quicker pace than node-webkit.
Upsides and Downsides
Chromium shells that embed Node.js–such as node-webkit and atom-shell–come with many upsides and downsides, especially when compared with "native" applications.
Pros
- Developing desktop applications using HTML, CSS and JavaScript, as a web developer, allows you to quickly push out functional, cross-platform applications using the frontend frameworks and npm modules you already know
- Access to the latest web technologies available in Blink
- Easy to package the application for end users
- If you already have an remote web application, you can already reuse most of that codebase to build a desktop application
Cons
- When packaging applications using either shell, the resulting executable contains an almost complete version of Chromium and Node.js as well as your HTML, CSS and JavaScript. Depending on the target system, the entire packaged application can become almost 100Mb, whilst the size of an application using native UI libraries can start at a mere few kilobytes in size.
- Compared to native applications, desktop web applications typically require a much larger amount of RAM and CPU power to run and render
- In terms of appearance – if you want to make your application look good on the platform you're planning on deploying to – you'll need to either recreate common UI elements using CSS or create a totally new UI, including a new design for every operating-system-provided UI element such as the title bar, menu bar and context menus.
Conclusion
Building desktop applications using HTML, CSS and JavaScript is looking promising, with applications such as Spotify, Light Table and Brackets making use of open source browser renderers. With node-webkit and atom-shell, it's easy for developers to port web applications to the desktop and use Node.js and npm modules within the frontend.