Home

Game 1: RiceRocks

Writing interactive games and animations is both instructive and fun. Writing such programs is, however, often challenging. Interactive programs are complex because the user, not the programmer, is in charge of the program’s execution: the program itself is often a passive receiver of commands, reacting only when some stimulus in the external world (whether a user’s keystroke or the tick of a clock) occurs.How to Design Worlds

My original goal with this game was to create a minimalist HTML5 arcade-style game to learn the basics. I translated a game I originally encountered many years ago in Rice University's Interactive Python Mooc called RiceRocks. This meant the sprites and sounds were all readily available on my PC.

Though translating the game from the original Python to JavaScript wasn't very difficult, it got me thinking on various philosophical issues, and I kept refactoring until it strayed far from the original.

Structured Design

Successful design is based on a principle known since the days of Julius Caesar: Divide and conquer.Structured Design, Edward Yourdon and Larry L. Constantine

To avoid a big ball of mud, the heart of good design is to compose things out of smaller pieces, which if well designed can then be re-used in other projects.

In software, each of these design units is a separate file. For this game, I split the design into one main script and three modules.

My lost love of JavaScript's module system

This learning project lead down quite a lot of passages which at first seemed very promising, but I subsequenly decided were blind alleys.

One of them was to use JavaScript's relatively recent (2015) support of modular programming, needing type="module" added:

  <script type="module" src="game1.js"></script>

By the way, this only works with served files. If you load an html file statically with a browser, it till complain of CORS errors if any of its JavaScript files are type="module".

I found it confusing that the client that loads modules gets labeled a module, and that the modules themselves are not listed in the html file, and it took some cursing and frustration to figure this out.

Anyways, the game1.js got all it needed from the three module files like this:

import { resizeListener, clearScene, drawState } from "./image.js";
import { playSound } from "./sound.js";
import { initState, updateState, uiListener } from "./state-loop.js";

The decision of what to put in each module Constantine termed cohesion. To me, it seemed natural to hide all the horrors of the Web Audio API in one file, exposed to the client as just one function, and similarly the horrors of the canvas in another.

Doing it like this turned into a good exercise on the power of encapsulation and information hiding. Unfortunately, it also made testing and documenting way harder. Furthemore, when I wanted to make these modules more general by getting them to read data from a JSON file, I found the silofication of these firm walls between modules resulted in unnecessary complexity.

So I changed my index.html to:

  <script src="image.js"></script>
  <script src="sound.js"></script>
  <script src="state-loop.js"></script>
  <script src="game1.js"></script>

Note the client file must be loaded after its modules.

Part of my motivation for this was wanting to move data such as the sound and image files to a JSON file to make it easy to change the facade of the game.

The guts of the application in game1.js consists of this infinite loop:

function animationLoop() {
  clearScene();
  drawState();
  playSound();
  updateState();
  window.requestAnimationFrame(animationLoop);
}

Note none of these functions have any arguments. This is due to using what Constantine termed global coupling.

I did this project to refresh my JavaScript knowledge after teaching myself Erlang where functions are not rewritable (or mutable in computer jargon). Erlang uses recursive infinite loops a lot, and these typically have a compound data structure conventionally called state which gets replaced each recursion.

From my notes, a loop which most Erlang programmers would not write themselves, but use the one provided by the OTP framework, looks something like this:

loop(Module, State0) ->
  receive
    {call, Pid, Ref, Request} ->
      {reply, Reply, State1} = Module:handle_call(Request, {Pid, Ref}, State0),
      Pid ! {reply, Ref, Reply},
      loop(Module, State1);
    {cast, Request} -> 
      {noreply, State1} = Module:handle_cast(Request, State0),
      loop(Module, State1);
    stop -> Module:terminate(normal, State0);
    Unknown ->
      {noreply, State1} = Module:handle_info(Unknown, State0),
      loop(Module, State1)
  end.  

A reason data is immutable in Erlang is because various processes are running in different cores or other computers, so they don't share memory.

JavaScript's Web Workers are Erlangish in that they communicate via message coupling.

Influenced by Erlang, I initially wrote state-loop.js as a web worker, and hit the snag that I couldn't see a way for the web worker to communicate directly with the animation loop. While the animation loop could post messages to the web worker, JavaScript doesn't appear to have Erlang's return address mechanism. The animation loop could possibly be made the web worker's callback function, but I'm not sure how.

Instead of gaining any efficiency from moving stuff to a separate thread, the animation got more jerky because of all the work involved in bouncing messages back and forth 60 times a second instead of simply sharing a reference pointer to a complex data type representing state. Furthermore, the order of the received messages was shuffled, leading to the mystery of the vanishing missiles.

So here I gained this insight:

In JavaScript, never create when you can mutate

Since map builds a new array, using it when you aren't using the returned array is an anti-pattern; use forEach or for-of instead. MDN's Array.prototype.map() page.

Due to my prejudice against global variables, my initial design was to have the modules share data via function arguments (This may fall under the jargon term semantic coupling, but as is inevitable in the IT industry, what was originally a nice, simple concept has been transformed into an unintelligible swamp of BS by consultants).

The canvas object created by image.js was imported by game1.js and accessed by other modules via a function argument. The state compound data structure which just about every function in every module needs was similarly shared from state-loop.js with other modules via function arguments.

Ultimately, I came to accept that by attaching the canvas and state objects to window — the root, global object in a browser which everything except a web worker can access — I saved a lot of typing and potential bugs from forgotten function arguments.

Testability

Testing such programs appears even harder because without the interaction there appears to be no program to speak of, but automating the testing of interactive programs is especially challenging. Developing clean, well-tested interactive programs therefore seems especially daunting to beginners.How to Design Worlds

Step 1 is to download the latest release. Unziping it unpacks ./SpecRunner.html along with various files in ./lib/jasmine-<version>/* which I copied into a subdirectory called ./test. It also unpacks examples in subdirectories called src and spec which don't need to be copied.

I edited SpecRunner.html, renamed ./test/index.html, to look like this:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v3.6.0</title>

  <link rel="shortcut icon" type="image/png" href="jasmine_favicon.png">
  <link rel="stylesheet" href="jasmine.css">

  <script src="jasmine.js"></script>
  <script src="jasmine-html.js"></script>
  <script src="boot.js"></script>

  <script type="module" src="unitTests.js"></script>
</head>

<body>
  <canvas id="board"></canvas>
</body>
</html>

The reason I added canvas to the body is that document.querySelector("#board") in image.js will otherwise return null, breaking all the ctx... statements. My unitTests.js file started like this:

import { resizeListener } from "../image.js";

describe("image.js", function() {

  beforeEach(function() {
    window.state = { "sprites": []
                   , "missiles": []
                   , "lives": 3
                   , "score": 0
                   , "scale": 1.0
                   , "noise": null
                   };
  });

  afterAll(function() {
    document.body.removeChild(document.querySelector("#board"));
  });

  it ("800x600 screen should set scale to 1.0", function() {
    spyOnProperty(window, "innerWidth").and.returnValue(800);
    spyOnProperty(window, "innerHeight").and.returnValue(600);
    resizeListener(800, 600, null);
    expect(window.state.scale).toBe(1.0);
  });
});

In the afterAll stanza, I remove the canvas so it doesn't mess up the screen Jasmine writes its repot on. The window size needs to be mocked to known values, else resizeListener will set scale by whatever the actual values of window.innerWidth and window.innerHeight happen to be.

resizeListener uses and auxiliary function, getScale, which isn't exported. During development, I start by writing tests for the auxiliary functions, and subsequently hide then and redo the tests with the exposed functions as above. This is possibly considered cheating by unit test purists, but works for me.

A reason I don't like Jasmine's convention of call the subdirectory holding the unit tests ./spec and giving calling its examples PlayerSpec.js and SpecHelper.js is that thinking unit tests are specifications seems to be a common misconception amongst the Agile crowd.

Programmers who advocate writing tests before writing code often believe those tests can serve as a specification. Writing tests does force us to think, and anything that gets us to think before coding is helpful. However, writing tests in code does not get us thinking above the code level. We can write a specification as a list of high-level descriptions of tests the program should pass essentially a list of properties the program should satisfy. But that is usually not a good way to write a specification, because it is very difficult to deduce from it what the program should or should not do in every situation. Who Builds a House Without Drawing Blueprints?, Leslie Lamport

Patterns

I've never studied the Gang of Four's 23 design patterns, but influenced by Robert Nystrom's Game Programming Patterns I decided to refactor my code.

Object Literal Pattern

This is not one of the GoF's 23 classics, but is one I use a lot.

Object Literals are comma separated key-value pairs between curly brackets which differ from traditional OOP classes in that they don't require instantiation using the new operator.

A quirk of mine — which result in warnings from jslint and jshint is addressing values stored in object literals as images["missile"] rather than images.missile. This is because images[Variable] is legal and images.Variable isn't, and remembering different conventions to achieve exactly the same thing is a waste of memory.

Another reason I like using double quoted keys is it helps the declarations of object literals double as legal JSON. Another quirk of mine is using leading commas, a habit I picked up from reading SQL code. Using leading commas makes it easier to balance parenthesis in deeply nested data structures — which mercifully I don't have any of in this example.

Refactoring to the six GoF design patterns Nystrom devoted chapters to resulted in more of my code getting moved into object literals.

Command Pattern

Encapsulate a request as an object, thereby letting users parameterize clients with different requests, queue or log requests, and support undoable operations. — GoF

This classified a a behvioural pattern, meaning it deals with communication between objects.

I originally wrote the dispatcher which translated the patterns of user input events as switch blocks with case statements in JavaScript.

After reading the chapter on command patterns in Nystrom and Addy Osmani's Learning JavaScript Design Patterns, I decided a more elegant way would be to write the pattern to be matched as

command["keydown"]["ArrowLeft"]

where "keydown" is the value of event.type and "ArrowLeft" the value of event.key of the KeyboardEvent object, so in theory this just meant calling a required action with

command[event.type][event.code]

In practice, the spacebar event.code " " had to be translated to "Spacebar" since it has to be a valid function name, for touch events the event.code needed to be obtained from event.target.id, plus command needed to be guarded from F12 and "non-game" keys, so uiListener still ended up filled with conditionals.

Furthermore, implementing this required me to learn about JavaScript's getter syntax which I hadn't used before. But ultimately, it led to more elegant code than my original two switch blocks, with the first having cases for event.type and the second for event.key. It also disciplined me to write action functions for different user commands instead of putting the statements in case blocks, resulting in an overly long dispatcher.

So conforming to the chain of command pattern (at least my interpretation of it), resulted in this nice template for future games:

const command = { "keydown": { get ArrowLeft() {return turnLeft(true)}
                             , get ArrowRight() {return turnRight(true)}
                             , get ArrowUp() {return burnRocket(true)}
                             , get Spacebar() {return fireMissile(true)}
                             }
                , "keyup":   { get ArrowLeft() {return turnLeft(false)}
                             , get ArrowRight() {return turnRight(false)}
                             , get ArrowUp() {return burnRocket(false)}
                             , get Spacebar() {return fireMissile(false)}
                             }
                };

Wikipedia's description lists four requirements:

command
eg "ArrowLeft"
receiver
eg turnLeft(true) — it executes the work requested by command
invoker
eg "keydown"
client
In this game, that's always the spaceship. But if there were more than one player controlled sprite, something like command["spaceship"]["keydown"]["ArrowLeft"] would be needed

Besides simply configuring input, which is all I've used the command object for here, Nystrom suggests also using it for undo and redo, which ties into Jakob Nielsen's usability top 10.

Flyweight Pattern

The pattern was first conceived by Paul Calder and Mark Linton in 1990 and was named after the boxing weight class that includes fighters weighing less than 112lb. The name Flyweight itself is derived from this weight classification as it refers to the small weight (memory footprint) the pattern aims to help us achieve. Learning JavaScript Design Patterns, Addy Osmani

This is the only structural design pattern listed here. Structural means it deals with object composition.

While RiceRocks has a dozen asteroids spinning at any given time, they all share the same blob of binary data stored in image.js as:

const images = { "background": new Image()
               , "debris": new Image()
               , "spaceship": new Image()
               , "asteroid": new Image()
               , "explosion": new Image()
               , "missile": new Image()
               };

I initially stored pointers to these binary blobs in each sprite as an attribute called image, but thanks to my experiment with web workers where references can't be shared and JavaScript wisely refuses to clone big blobs of binary, I realise all the sprite needed was a string corresponding to a key in the image dictionary.

The same applies to sound.

I discovered I'd already unwittingly implemented the flyweight pattern by not duplicating blobs read from png or ogg files, storing them once in dictionaries and refencing them via short string names.

Observer Pattern

One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves. — GoF

This is a behavioral pattern, dealing with communication between objects.

There's quite a lot of overlap between the GoF terminology here and event-driven programming or reactive programming.

In GoF terminology, observers can attach themselves — done in JavaScript with target.addEventListener(type, listener) which I've used a lot — or detach using target.removeEventListener(type, listener) which I've found no need for yet.

window.addEventListener("DOMContentLoaded", setup);
window.addEventListener("unload", cleanup);
window.addEventListener("resize", (event) => resizeListener(BASE_WIDTH, BASE_HEIGHT, event));
document.addEventListener("keydown", uiListener);
document.addEventListener("keyup", uiListener);
document.querySelector("#upButton").addEventListener("pointerdown", uiListener);
document.querySelector("#upButton").addEventListener("pointerup", uiListener);
document.querySelector("#leftButton").addEventListener("pointerdown", uiListener);
document.querySelector("#leftButton").addEventListener("pointerup", uiListener);
document.querySelector("#rightButton").addEventListener("pointerdown", uiListener);
document.querySelector("#rightButton").addEventListener("pointerup", uiListener);
document.querySelector("#spaceBar").addEventListener("pointerdown", uiListener);
document.querySelector("#spaceBar").addEventListener("pointerup", uiListener);

In JavaScript, "message" events used for multithreading by Web Workers, and distributed computing via WebSockets and Service Workers — none of which are used in this simple game, but will become important later — would also fall under the observer pattern.

Prototype Pattern

This is another creational pattern, and fortuitously, before learning this, I called my three functions which use it createSpaceship(), createAsteroid(), and createMissile().

These all return an object literal conforming to a template I've called Sprite, which is defined by documentation using conventions I've tried to establish with JSDoc rather than any programing code.

There's no createExplosion() because the spaceship and asteroids only temporarily become explosions, and the Object.assign(sprite, createAsteroid()); statement used to clone a replacement asteroid for one hit by a missile is possibly closer to the GoF's definition.

Singleton Pattern

There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point. — GoF

This is a creational pattern, and I've attached two objects to the window objects so that all functions in all modules can access their attributes: state and canvas whose width and height is needed in various places — making them singletons.

Global state is generally considered an anti-pattern, and Nystrom's chapter on singleton is mainly a warning to avoid it, if you can.

The goal of removing all global state is admirable, but rarely practical. Most codebases will still have a couple of globally available objects, such as a single Game or World object representing the entire game state. Nystrom

So other than renaming state as game or world, there doesn't really seem much alternative to making it global.

State Pattern

The GoF clasify this as a behavioural pattern.

Rather than just the singleton holding the state at a given tick, this broader, involving the rules of the game as in what transitions are legal from a given node.

Documenting

Most languages come with a large collection of abstractions. Some are contributions by the language design team; others are added by programmers who use the language. To enable effective reuse of these abstractions, their creators must supply the appropriate pieces of documentation — a purpose statement, a signature, and good examples — and programmers use them to apply abstractions.

After testing, the next hardest part fro me has been getting to grips with JSDoc.

I've also tried to follow the Google JavaScript Style Guide, with the exception of ignoring its views of trailing commas since I find leading commas far better.

Audio

In my first draft of this project when I struggling to understand the myriad Web APIs, I kept one hierarchical diagram with links to documentation on the many objects and their methods, properties and events I was using. The audio part looked like this.

├── BaseAudioContext
│   ├── Constructor
│   │   └── AudioContext()
│   └── Properties
│       └── destination
└── AudioBufferSourceNode
.   ├── Constructor/Instantiator
.   │   └── BaseAudioContext.createBufferSource()
.   ├── Properties
.   │   ├── buffer
.   │   └── loop
.   └── Methods
.   .   ├── connect()
.   .   ├── start()
.   .   └── stop()

I'm more of a graphics than sound guy, and I found all the objects required for audio extremely confusing. It's a bit analogous to graphics in that files containing binary blobs in various formats need to downloaded by the browser and stored in memory to be referenced by named constants.

A context is needed which is conventionally made a global constant whose methods are used to make noise when desired.

Thanks to modularisation, I could move all this complexity into a separate file to be access in my main file by

import { playSound } from "./sound.js";

where sounds is a key-value store of the four sounds used in the game which can be passed to one function as in

playSound("missile");

Graphics

One subtledy with making the images promises to be called via .then chains rather than directly was the writing for lives and scores vanished. The reason was the order that the background got rendered was no longer predictable, so the writeText() function had to be called from within the paintScene(imageName) then block.

One of the advantages of modularity I discovered here is it that it improves testability, which I find fairly tricky in a game like this.

Though I couldn't abstract image.js down to just two public names, I similary managed to hide much of this complexity in a module accessed with a dictionary of key-value pairs and some functions.

├── Canvas
│   ├── HTMLCanvasElement
│   │   └── Properties
│   │   .   ├── width
│   │   .   └── height
│   └── CanvasRenderingContext2D
│   .   ├── Constructor/Instantiator
│   .   │   └── HTMLCanvasElement.getContext()
│   .   ├── Properties
│   .   │   ├── fillStyle
│   .   │   └── font
│   .   └── Methods
│   .   .   ├── clearRect()
│   .   .   ├── drawImage()
│   .   .   ├── fillText()
│   .   .   ├── restore()
│   .   .   ├── rotate()
│   .   .   ├── save()
│   .   .   └── translate()
└── HTMLImageElement
.   ├── Constructor
.   │   └── Image()
.   └── Properties
.   .   ├── width
.   .   ├── height
.   .   └── src

Coding

Learning coding is like playing cards — you learn the rules, then you play, then you go back and learn the rules again, then you play again. — From Mozilla's Web audio concepts and usage guide.

Web APIs

Frontend web development boils down to familiarisation with the bewildering choice of Web APIs available, and the goal of this first game was to learn a minimal subset to get animation, sound, and user interaction working.

Invariably there are at least four ways of doing anything with JavaScript, and guessing the correct one involves knowing whether it is old or new, good or bad, functional or object-oriented, synchronous or asynchronous, block or function scope...

Achieving a desired goal requires an encyclopaedic knowledge of many objects and their properties, events and their handlers, and methods and their parameters — mercifully MDN is an excellent reference.

These are the ones I've settled on for now. Making the game mobile friendly involved learning yet another Web API, Touch Events, which I decided not to add the diagram below since it was too crowded already.

I've attempted to draw a hierarchy of the objects I've used, though in JavaScript that's not easy. For instance, though Document is a child of Window, and CanvasRenderingContext2D of HTMLCanvasElement etc, coding convention is to treat each as a separate object. AudioContext is initially addressed as a child of Window since it's the global, root object.

├── DOM
│   ├── Window
│   │   ├── Properties
│   │   │   ├── innerWidth
│   │   │   └── innerHeight
│   │   ├── Events
│   │   │   ├── DOMContentLoaded
│   │   │   ├── unload
│   │   │   └── resize
│   │   └── Methods
│   │   .   └── requestAnimationFrame()
│   └── Document
│   .   ├── Events
│   .   │   ├── keydown
│   .   │   ├── keyup
│   .   │   ├── pointerdown
│   .   │   └── pointerup
│   .   └── Methods
│   .   .   └── querySelector()

The entry point of my programme introduced me two notorious horrors of Javascript: the Pyramid of Doom (also known as Callback Hell); and the White Screen of Death, neither problem I've entirely solved.

I toyed with putting the code below into a function called init(), but decided to simply leave it in the body of the script because making it a separate function didn't DRY or KISS anything. Furthermore, placing initialisation steps in a block starting with window.addEventListener("DOMContentLoaded", ... seems more in keeping with event-driven programming than creating an extraneous function.

window.addEventListener("DOMContentLoaded", async function (event1) {
  await backgroundImage.addEventListener("load", function (event2) {
    resize();
    sprites[0] = createSpaceship();
    for (let rock = 1; rock <= 13; rock++) { 
      sprites[rock] = createAsteroid();
    }
    window.addEventListener("resize", resize);
    window.requestAnimationFrame(loop);
  });
});

A problem the above code addresses is I want my website to have responsive design in that the board (ie canvas object) should use all the available screen real estate. To calculate this, the programme needs window.innerWidth and window.innerHeight, which if used before the window has completed loading, will be undefined.

The base size (ie scale = 1.0) is provided by the width and height of the background image, which if read before the graphic file has finished downloading will be undefined.

A pitfall most novices will fall into is that, without care, JavaScript will produce a garbage value for scale using undefined for its calculation which canvas won't draw, leading to the dreaded White Screen of Death with no error messages or clues on the console.

Before unravelling the above code, lets look at the new way, promises, using fetch which I've written the function to download sound files with:

function loadSound(url, audioNode) {
  fetch(url)
  .then((response) => response.arrayBuffer())
  .then((buffer) => audioCtx.decodeAudioData(buffer))
  .then((decodedData) => audioNode.buffer = decodedData);
}

Three asynchronous processes which are contingent on each other — the buffer property of the audio node needs data decoded by the audio context, which in turn depends on a sound file that has to have finished downloading. The .then(...) syntax lets us chain these three steps together in sequential steps, hiding the mind-bending concurrency.

The old way of doing this was to nest each subsequent asynchronous step in the callback of its predecessor, which for many contingent steps would lead to very deeply nested, ugly code — the Pyramid of Death.

Mercifully, the initial window then background image loaded before launchign the loop chain only involves two steps, so its pyramid of doom isn't very high. Preventing a white screen of death, however, involved the additional step of invoking JavaScript's async function (...) {... await ...} combination to avoid the loop getting started before scale was set correctly.

A fourth event, resize, is attached to the window so the user can make the board bigger or smaller by adjusting the size of their browser, or changing the orientation of their phone.

How to stop?

Frameworks which have an init() or setup() starting point tend to have a stop(), terminate(), or cleanup() function to call at the end. I discovered I needed something like that after testing my site on a mobile phone and discovering that when I closed a window but didn't shut down the browser, the sound continued. I attempted to fix this with yet another event handler.

Again, in keeping with my home brewed event-driven programming style, everything goes into a main body code stanza starting with window.addEventListener("unload",... rather than a function.

window.addEventListener("unload", function (event) {
  backgroundSound.stop();
  thrustSound.stop();
  missileSound.stop();
  explosionSound.stop();
});

game1.js

A simple arcade game translated to JavaScript from a Python Mooc given by Rice University.
Author:
  • Robert Laing
Source:

Requires

  • module:rice-rocks.js

image.js

Module holding audio and image data and functions
Source:

sound.js

Module simplifying JavaScript's audio to a public interface of an images dictionary and playSound function
Source:

state-loop.js

Module that updates state according to clock ticks and user input
Source: