NW.js is a framework for building desktop applications with HTML, CSS, and JavaScript. It works by combining a JavaScript app with Node.js and Google’s Chromium browser engine into a single desktop program.
I’m a frontend developer with little experience in creating desktop applications and installers, so it took me some time to find all the details of how to set everything up. This tutorial is an overview of my findings. I hope it can help you to get started using NW.js and to create desktop programs and installers. I’ve written this tutorial using MacOS 10.14 (Mojave), Ubuntu 18.04 and Windows 10.
Electron is the better known of the frameworks for creating native applications. I also found it easier to use. My app however relies on requestAnimationFrame
for timing, and I couldn’t use Electron because of an issue in Chromium (the timer stops when the app window is hidden). In the end however NW.js is not that hard to use.
The app for this tutorial is a simple metronome. Useful for for musical timekeeping. It uses requestAnimationFrame
and it generates sound, so it will be easy to demonstrate that with NW.js the requestAnimationFrame
timer will keep going even if the app’s window is hidden behind others. See a demo of the app here: https://hisschemoller.github.io/nwjs-tutorial/src/.
There’s quite a lot to go through, so here’s an overview of what’s included in this tutorial:
Clone the Git repository for the tutorial at https://github.com/hisschemoller/nwjs-tutorial.
Install the dependencies as usual and start as defined in package.json
.
yarn install
yarn start
The app will now be available at http://localhost:3000
Open the URL in a browser and you’ll see a small counter display. Click the Play button. The counter increases and a short blip sounds at each counter increase.
Note that the metronome stops if you switch to another browser tab, or if the browser is completely hidden by other windows.
During development an app can easily be tested within the NW framework from the command line. To set this up requires just a few steps:
Two flavors of NW exist: For development purposes there’s an SDK version which contains the Chrome developer tools. The regular version lacks the developer tools and because of that results in a smaller file size. To use the SDK version add -sdk
to the version string.
Add NW as a devDependency in package.json_:_
"devDependencies": {
"nw": "0.38.0-sdk"
}
NW needs a manifest file to run. The manifest file is a JSON file called package.json
. So confusingly it’s named exactly the same as NPM’s package.json
. The difference however is that the manifest file is located in the same directory as the app’s source files, while NPM’s package.json
is at the root of the project.
+-- nwjs-tutorial
| +-- package.json (NPM)
| +-- src
| | +-- index.html
| | +-- package.json (NW manifest file)
As a bare minimum the manifest just needs the fields name
and main
for NW to run. The main
field points NW to the app’s entry point, which in this case is index.html
.
A lot more settings are possible however. a reference of all available options can be found in the NWjs Manifest Format Reference.
This is the manifest for the Metronome app:
{
"name": "Metronome_manifest_name",
"description": "A metronome app to keep time.",
"main": "index.html",
"chromium-args": "--disable-raf-throttling",
"window": {
"icon": "img/icon.png",
"height": 240,
"resizable": false,
"width": 400,
"title": "Metronome_manifest_window_title"
}
}
I’ve added a script in package.json
to run NW, but it could as easily be started by just typing yarn nw
in the command line.
"scripts": {
"start-nw": "nw --disable-raf-throttling"
},
Once started, NW looks for the "main"
field in package.json
to find the app’s entry point, and looks for the manifest file in the same directory as the entry point. It then has all the data it needs to run the app.
Try running NW with and without the --disable-raf-throttling
command line option and notice that with the option the metronome keeps running when the app is hidden.
To create a desktop program a build of NW can be downloaded from the nwjs.io download page at https://nwjs.io/downloads/. Builds are available in different flavors, for Mac, Linux and Windows, 32 or 64 bit, with or without the SDK option.
In general to create a desktop program you will add the project’s source files to the downloaded NW build, and the resulting package is the program to distribute. There are differences however between Mac, Linux and Windows. NW.js has it’s own documentation here.
nwjs.app
(among others)./src
directory into a zip file and rename the zip to app.nw
. So, to be clear, the file extension will be .nw
instead of .zip
, and it will contain index.html
, the css
, js
and fonts
directories and the package.json
manifest file.app.nw
inside the downloaded Mac release, in the nwjs.app/Contents/Resources/
directory. (right click on nwjs.app and choose ‘Show Package Contents’ to open it)/assets/icons/metronome.icns
to app.icns
and paste it into nwjs.app/Contents/Resources/
. The file must replace the existing default icons. See below to create an .icns
file.nwjs.app/Contents/Resources/documents.icns
with the .icns
file.The file nwjs.app
is now an executable that runs the app. It’s the only file you need, you can delete all the other files in the download. Rename the file to Metronome.app. Double click to run it.
On a Mac you can create an .icns
file with the iconutil
command line utility. Here are two tutorials:
On MacOS Mojave I created the icon files as described in the first tutorial and ran iconutil
as described in the second one. That worked. A general overview of the steps:
png
images. (For this tutorial they’re in assets/icons/metronome.iconset
)..iconset
. In this case metronome.iconset
.cd
to the directory where the .iconset
is.iconutil
to generate the .icns
file, like this:iconutil -c icns metronome.iconset
I have tested this on Ubuntu 18.04, but I don’t know in how far the process is different in other Linux distributions.
/src
directory of the project into the root directory on the downloaded package. In my case it’s called nwjs-sdk-v0.38.0-linux-x64
. So your source files and package.json
manifest file will share the same directory with the nw
file in the download./metronome.desktop
file to the same root directory as the source files and manifest. See below for creating a .desktop
file.In GNOME and other freedesktop.org-compliant desktops, an application gets registered into the desktop’s menus through a desktop entry, which is a text file with .desktop extension. This desktop file contains a listing of the configurations for your program.
The file should have a unique descriptive filename without spaces. For this project I use metronone.desktop
. The location should be, to be accessible by everyone:
/usr/share/applications
Or, to be accessible to a single user:
/.local/share/applications
Some .desktop file resources:
Download a Linux package and copy the source files, manifest and icon file as in the steps of ‘Create a Linux desktop program’ above.
/opt
directory.metronome.desktop
file to /usr/share/applications
.To copy to the source files directory and the desktop file use the cp
command with administrator rights in a terminal:
sudo cp -r /path/to/metronome /opt
sudo cp /path/to/metronome.desktop /usr/share/applications
You will now be able to find and run the app just like any program you’ve installed. No restart or anything needed.
/src
directory of the project into the root directory on the downloaded package. Your source files and package.json
manifest file should be in the same directory as the nw.exe
file.Double click nw.exe to run the app.
Windows uses icons of the .ico
file type. Online converters exist that can generate an .ico
file from another image type. For example ICOConvert at https://icoconvert.com/.
More about Resource Hacker:
I’ve divided this section into two parts. The first is to create a reusable nice looking template for the installer that shows the app and a shortcut to Applications
so users can easily add the app to the Applications
directory.
The second part is the creation of the actual installer.
installerTemplate.dmg
.nstallerTemplate.dmg
to open it. It will show up in finder as a device.That’s it for creating the template file. Now open the template and make it look nice:
metronome.app
file into the folder.Applications
into the folder./assets/mac/mac-installer-background.jpg
.)InstallerTemplate.dmg
just created.metronome-installer_1.0.dmg
.DMG creation resources:
I’ve used an easy to follow tutorial that you can find here: https://ubuntuforums.org/showthread.php?t=910717. The steps are as follows:
control
with information for package management tools like dpkg
to manage the package. I’ve already added the file for this project in /assets/linux/
.<project>_<major version>.<minor version>-<package revision>
. So here that’s metronome-installer_1.0
.deb
installer with dpkg-deb --build metronome-installer_1.0
.This is the directory and file structure:
+-- metronome-installer_1.0
| +-- DEBIAN
| | +-- control
| +-- usr
| | +-- share
| | | +-- applications
| | | | +-- metronome.desktop
| +-- opt
| | +-- metronome
| | | +-- nw
| | | +-- index.html
| | | +-- package.json
| | | +-- ... (and all the other files of the application)
As mentioned, create the .deb
file with:
dpkg-deb --build metronome-installer_1.0
The result is a file named metronome-installer_1.0.deb
. The Metronome app can then be installed with:
sudo dpkg -i metronome-installer_1.0.deb
Another deb
installer tutorial:
INNO Setup is voted best here at Slant, so that’s what I decided to use.
For an overview of the wizard steps please have a look at this tutorial’s GitHub Readme, as this Medium post can’t show the nested list structure I used for the overview. Or read a tutorial with lots of helpful screenshots here.
The compiler will take a bit of time to create the installer, but you’ll be able to follow the process in the ‘Compiler Output’ pane of Inno Setup.
The resulting installer is an .exe
file that presents users with the installation procedure they’re familiar with.
INNO Setup resources:
And finally, some other articles that I found useful to get familiar with NW.js:
☞ How to create the Node.js API with TypeScript and GraphQL
☞ How to set up automatic backups for MongoDB using Node.js
☞ Top 5 Content Management System (CMS) Built using Node.js
#node-js #javascript