Growing an HTML5 game engine

Growing an HTML5 game engine

By Craig Robinson

March 13th 2014 at 10:00AM

Absolute Hero's Craig Robinson highlights the benefits of building your own development platform for web games

[Craig Robinson is the co-founder of Absolute Hero, an independent game studio based in Seattle, Washington. He previously built games for FlowPlay, GameHouse, and RealArcade.]

Last year we had the opportunity to create a series of five Mahjong-link games in HTML5 for Spil Games. The first game we built was Dream Pet Link. Given that we were tasked with building four additional games with the same mechanic but different themes, we assumed we'd be able to reuse much of the same code, that we could just write the first game and then swap in new art and sound assets for the subsequent titles.

Oh, only if things were so easy.

There were a few issues that made things more complicated:

* Desire to reuse some code in other games that didn't have the same mechanic

* Uncertainty about code changes necessary to accommodate differences in the art and audio

* Features and performance improvements we knew we would discover later in the cycle

These considerations forced us to take a different design approach than we had initially anticipated. Here I will share with you the process we went through to build these games while creating code libraries for use in these and our other HTML5 games. As you will see, the process was fluid and organic. I will also describe why JavaScript proved to be an ideal language to support our work.

Start your engines

We decided that functionality would be organised into three distinct buckets;

1) Code and configuration files specific to a given game

2) Code that could be shared by all the games with this mechanic (we called this LinkEngine)

3) Code that could be used by any game (we called this the Absolute library).

It wasn't always immediately clear into which bucket to place a particular functionality. Sometimes we would build a feature, and only after working with it for a while, realise that it could be made more generally useful. We would then promote it to the next level of abstraction and move it to a different bucket.

We didn't start to split out game specific code from LinkEngine until it was time to build the second game. By that time we felt like we had a pretty good handle on what needed to be in the Absolute library, but splitting the game specific code from LinkEngine was a process that, as we describe below, required some additional effort that we believe provided a better result than if we had tried to make this separation in advance.

What do you mean you want to use a larger font?

One thing we didn't address in the first game was how changes to the graphic assets would affect the layout of on screen items. Some of the graphics were positioned relatively, but others had fixed/hardcoded locations. This scheme broke down when we built the second game. The theme of each game was different and our artists chose a different font (with different metrics) for text in each game. Other asset sizes were changed as well such as the logo for the game, buttons, dialog box frames, progress bars, etc.

In order to accommodate these different sizes we changed some asset positioning from fixed to relative. In situations where that wasn't appropriate, we created a game specific JSON layout file that provided coordinates for various assets. We wrapped this functionality in a Layout class that hides the complexities from the rest of the code.

One might argue that we should have foreseen the need to support more flexible layouts up-front and we could have saved ourselves some effort by planning for differently sized assets in advance. In fact, we did anticipate this issue, but we chose not to address it up-front. The game design was very fluid, we didn't know which features and items would stay in the game and which would go, and we knew we could easily re-factor this later.

By putting off the work to build a more flexible layout mechanism we ended up with a more elegant solution, because we were able to collect more information about the type of features and functionality required across a number of scenarios.

Sounds about right

Each game had unique sound effects with different lengths, but we were able to use these without code changes. We stuck to the same structure; the same actions caused sound effects and we had a couple of different music tracks for different parts of the game. A JSON configuration file allowed us to easily re-map existing actions to new audio assets.

We made significant improvements to our audio capabilities in between the first and second games, so we wanted to work these in. We added the ability to work with audio sprites, which allowed us to place sound effects with lower latency on devices without Web Audio API support. We used a Grunt (JavaScript automation tool) task to automate the creation of audio sprite files from our existing audio assets.

We now have an audio library that provides scalable support across platforms by taking advantage of high-end capabilities where available, and falling back to less rich, but still acceptable functionality, where not.

Making it better

We introduced a number of bug fixes, optimisations and feature improvements throughout the process. While each bug fix in the library code benefitted all the games using the library, each feature addition and bug fix required testing across all games.

Introducing changes to the libraries once we had completed the first game could have been an arduous process -- akin to performing maintenance on a car while driving -- but we benefitted from having JavaScript as our development language.

Why JavaScript aided this process

JavaScript has its problems and these have been well documented elsewhere, but there are some great things about JavaScript as well, including:

 * Loose typing

 * Object literal notation

 * Ubiquity & immediacy

 * JSON

Strongly typed languages allow the compiler to catch errors and perform optimisations that can't be performed in a loosely typed language. Loose typing has been criticised for these reason, however, it allows for much more flexibility in type conversion and class/object definition. These attributes help with prototyping and refactoring, even at later stages of development.

Another example is JavaScript's object literal notation. In JavaScript you can create an object by just enumerating its contents. This is extremely useful when experimenting with new features or in cases where a more formal object/class definition is unnecessary.

JavaScript is an interpreted (or JIT compiled) language and runs in every modern web browser. That means that the code/test cycle can be very fast. You see the results of your changes right away and this immediacy allows for rapid iteration on features and architecture. You still need to do thorough testing, but in terms of quick feedback and feature validation, I know of no better environment.

JSON, which is inspired by JavaScript's object literal notation and is native to JavaScript, is an ideal format for creating configuration files that non-developers can easily modify. We use JSON configuration files for level configuration, sound design, graphic layout, animation and many other things.

The good (and bad) attributes of JavaScript are thoroughly discussed in Douglas Crawford's excellent book, "JavaScript: The Good Parts." I highly recommend it if you are interested in doing HTML5 game development.

Refactoring vs. pre-factoring

Most experienced developers avoid pre-optimising code, rather, they build functionality and then look for bottlenecks, spot optimising where necessary to achieve desired performance. That doesn't mean they don't think about writing efficient code up-front, just that they don't spend more time than warranted before they know they need to.

Our design and development approach was similar. We certainly thought about architectural issues up-front and made some high-level decisions, but we didn't get hung up on making functionality abstract and re-usable until we understood the value of doing so. We were able to play with design concepts and iterate quickly, with very little cost. JavaScript supported this process and made the development experience flexible and fun.

We ended up with five great HTML5 games with very little game-specific code, a common engine for all the link games, a library to support audio playback across a variety of devices, and a general HTML5 game engine on which we can build any type of game. 

Most important, the number of game plays is in the millions and we’re seeing great loyalty in the game. Players have logged in about 1.4 million hours of play and the time they spend in the games has doubled in the last couple of months. Needless to say, we will continue to use this methodology as we move on to our next titles.