DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Electron Adventures: Episode 77: Cookie Clicker Game

The series has been going for a while now, and somehow I never got to the key subject of packaging. The main appeal of Electron apps is that they can be packaged and distributed, so the users can run them without having to install any dependencies. Even something as seemingly simple as "install Java" invariably leads to endless tech support issues.

I want to try two scenarios, and then try to package them. First, a purely static app. And then an app with compiled SPA backend like Svelte or React.

So before we get to packaging, let's build a simple static app - cookie clicker game. To have a few different types of assets to package we'll use:

The game will be totally trivial. Click the cookie, it plays sound and increases score.

Setup

$ npm install --save-dev electron jquery
Enter fullscreen mode Exit fullscreen mode

package.json

{
  "devDependencies": {
    "electron": "^15.1.1",
    "jquery": "^3.6.0"
  },
  "scripts": {
    "start": "electron ."
  }
}
Enter fullscreen mode Exit fullscreen mode

index.js

We just need to open index.html. I left empty preload.js just in case we need it for something.

let { app, BrowserWindow } = require("electron")

function createWindow() {
  let win = new BrowserWindow({
    webPreferences: {
      preload: `${__dirname}/preload.js`,
    },
  })
  win.loadFile("index.html")
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
Enter fullscreen mode Exit fullscreen mode

index.html

The cookie will be in the body, and #score will display the score. That's the whole game!

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="app.css">
    <title>Cookie Clicker</title>
  </head>
  <body>
    <div id="score">Score: 0</div>
    <script src="./node_modules/jquery/dist/jquery.js"></script>
    <script src="./app.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

app.css

To prevent some complications depending on where exactly the user clicks, we need to tell the browser we don't want the score to be clickable (pointer-events: none) or selectable (user-select: none).

body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-image: url("cookie.jpg");
  background-size: cover;
  background-repeat: no-repeat;
  height: 100vh;
  width: 100vw;
}

#score {
  pointer-events: none;
  user-select: none;
  font-size: 36px;
  font-family: fantasy;
}
Enter fullscreen mode Exit fullscreen mode

app.js

And finally the game logic. The only non-obvious thing is that we need to set audio.currentTime = 0. If user clicks while the sound is playing, we want it to start the sound from the beginning. Having just audio.play() without rewinding there would ignore the call, and that's a much worse experience.

Alternatively we could play multiple sounds at once, but that could be quite jarring if someone clicks really fast.

let score = 0

let audio = new Audio("coin.wav")

$("body").on("click", (e) => {
  e.preventDefault()
  score += 1
  $("#score").text(`Score: ${score}`)
  audio.currentTime = 0
  audio.play()
})
Enter fullscreen mode Exit fullscreen mode

Results

Here's the results:

Episode 77 Screenshot

Now that we have a simple static app, in the next episode we'll try to package it into something users can just download and run without installing any dependencies.

As usual, all the code for the episode is here.

Discussion (0)