Recipe: standalone desktop app of your game with electron
In this article we will set up our own electron build environment which we can use to build a native desktop app of our game.
We will also provide a config.json
file, that the app will read on start up so we can adjust the resolution, window mode and other stuff after the app has been built.
0. What is electron
Electron is a framework developed by GitHub that allows you to build cross-platform desktop apps with web technologies such as HTML, CSS, and JavaScript.
The idea behind Electron is to allow developers to create desktop applications using the web technologies they already know, rather than having to learn different programming languages and technologies for different platforms. Electron applications can be packaged and distributed for Windows, macOS, and Linux.
Electron does this by providing a runtime that wraps your web application code, along with a version of the Chromium web browser, and Node.js. This combination allows you to use Node.js APIs in your application, which provides much more control and flexibility than is usually available in a web browser environment.
Electron has been used to create many popular applications such as Visual Studio Code, Slack, Atom, and Discord.
1. Install Electron
To install Electron, you first need to initialize your project using npm. Go to your project directory using command prompt and then run the following commands:
npm init
Fill out the prompts to initialize your project, e.g.:
package name: (electron) my_game
version: (1.0.0)
description: An Awesome Game
entry point: (index.js) main.js <---- make sure you name this main.js as we will name our main script like that
test command:
git repository:
keywords:
author: Game Dev
license: (ISC) MIT
About to write to package.json:
{
"name": "my_game",
"version": "1.0.0",
"description": "An Awesome Game",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Game Dev",
"license": "MIT"
}
Is this OK? (yes)
After you're done, install Electron:
npm install --save-dev electron
2. Creating the Main Script
Next, create a new file in the root directory of your project and name it "main.js". This will be the entry point into your app.
Here is a basic main.js file:
const { app, BrowserWindow } = require('electron')
function createWindow () {
let win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
}
})
win.loadFile('game/index.html')
}
app.whenReady().then(createWindow)
This script creates a new browser window with node integration enabled and then loads your index.html file.
3. Updating your package.json
Your package.json should look something like this (based on the input you provided for the init prompt):
{
"name": "my_game",
"version": "1.0.0",
"description": "An Awesome Game",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Game Dev",
"license": "MIT",
}
replace the following:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
with this:
"scripts": {
"start": "electron ."
},
4. Running Your Application
At this point, you can run your application by using the command:
npm start
At this point, you wont see much, just an empty window if everything went well.
5. Packaging Your Application
To distribute your application, you'll need to package it into an .exe file. One of the most popular tools to do this is electron-packager. You can install it using npm:
npm install --save-dev electron-packager
After it's installed, you can add a script to your package.json to run it:
"scripts": {
"start": "electron .",
"build": "electron-packager . --overwrite"
}
Then, you can package your app by running:
npm run build
This will create a new folder in your project directory with the packaged application.
6. Adding a config.json
In order for your app to read configurations like window mode, resolution etc. you have to create a config file that your app can read at start up. We will call it config.json
.
Create a config.json
in the project root folder.
Add the following content:
{
"screenMode": "windowed_resizeable",
"resolution_width": 1280,
"resolution_height": 720,
"backgroundThrottling": true
}
To read the config.json
file and use the configuration in your Electron script, your main.js
should look like as follows:
const { app, BrowserWindow } = require('electron')
const fs = require('fs');
function createWindow() {
const path = require('path');
const configPath = path.join(path.dirname(app.getPath('exe')), 'config.json');
let rawdata = fs.readFileSync(configPath);
let config = JSON.parse(rawdata);
let screenMode = config.screenMode;
let width = config.resolution_width;
let height = config.resolution_height;
let backgroundThrottling = config.backgroundThrottling;
let windowOptions = {
width: width,
height: height,
webPreferences: {
nodeIntegration: true,
backgroundThrottling: backgroundThrottling
}
}
if (screenMode === "fullscreen") {
windowOptions.fullscreen = true;
} else if (screenMode === "borderless") {
windowOptions.frame = false;
} else if (screenMode === "windowed_resizeable") {
windowOptions.resizable = true;
} else if (screenMode === "windowed_static") {
windowOptions.resizable = false;
}
let win = new BrowserWindow(windowOptions);
win.setMenu(null);
win.loadFile('game/index.html')
win.webContents.on('devtools-opened', () => {
win.webContents.closeDevTools();
});
}
app.whenReady().then(createWindow)
Note: do not run the app yet, it won't work until the next step
7. Adding Your Game to the Electron App
Now all the prerequisites are fullfilled. Now you can add your actual game:
Export your game from microStudio as Html5 game
Create a folder called
game
in the root folder of your electron project and copy your exported game files into it. So that theindex.html
is located ingame/index.html
run the build command:
npm run build
. This will create a new folder with the built executableCopy your
config.json
into the newly created folderdone :) you now have a build system that you can use to create a native desktop executable of your game
8. Summary
Your main.js
should now look like as follows:
const { app, BrowserWindow } = require('electron')
const fs = require('fs');
function createWindow() {
const path = require('path');
const configPath = path.join(path.dirname(app.getPath('exe')), 'config.json');
let rawdata = fs.readFileSync(configPath);
let config = JSON.parse(rawdata);
let screenMode = config.screenMode;
let width = config.resolution_width;
let height = config.resolution_height;
let backgroundThrottling = config.backgroundThrottling;
let windowOptions = {
width: width,
height: height,
webPreferences: {
nodeIntegration: true,
backgroundThrottling: backgroundThrottling
}
}
if (screenMode === "fullscreen") {
windowOptions.fullscreen = true;
} else if (screenMode === "borderless") {
windowOptions.frame = false;
} else if (screenMode === "windowed_resizeable") {
windowOptions.resizable = true;
} else if (screenMode === "windowed_static") {
windowOptions.resizable = false;
}
let win = new BrowserWindow(windowOptions);
win.setMenu(null);
win.loadFile('game/index.html')
win.webContents.on('devtools-opened', () => {
win.webContents.closeDevTools();
});
}
app.whenReady().then(createWindow)
And your package.json
should look something like this:
{
"name": "my_game",
"version": "1.0.0",
"description": "An awesome game",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-packager . --overwrite"
},
"author": "Awesome Gamedev",
"license": "MIT",
"devDependencies": {
"electron-packager": "^17.1.1",
"electron": "^24.3.1"
}
}
Note:
electron-packager
- andelectron
version may vary.
Your folder structure
should look like this:
electron_app_root
├── game
│ ├── index.html
│ └── ... (other game files and folders)
├── my_game (folder that has been built by running `npm run build` (folder name varies depending on your project name))
│ ├── my_game.exe
│ ├── config.json
│ └── ... (other genereated files)
├── node_modules
├── config.json (this file has to be copied manually into the output folder (my_game))
├── main.js
├── package-lock.json
└── package.json