Home > Programming > JS1k: Making a very small game in JavaScript part 2 – optimisations

JS1k: Making a very small game in JavaScript part 2 – optimisations

December 2nd, 2010

This is part 2 of a series. See part 1 here.

Two types of optimisation

There are three types of optimisation when you’re trying to reduce the size of the code. The first two are “defactoring” techniques: they change the code but not the functionality. The third changes code and functionality.

1. Optimising for the compiler

One of the major optimisations the Closure compiler carries out is the renaming of identifiers. Closure will, wherever possible, rename your variables and functions with one letter names. This means you can keep using the nice long descriptive names and know that in the final file they won’t take up any more space than if you called everything x and y. So, how can you help with this?

One tiny optimisation I discovered was that I was using the literal 50 in a number of places. I defined a new variable fifty in the global scope and assigned it the value 50. Replacing all appearances of 50 with fifty increased the size of my un-minified code, but when minified, fifty became a single letter identifier and saved me a byte each time. Of course, there’s some extra code overhead to allow this, so there have to be enough occurrences for it to be worth it. Even a single byte’s worth it though.

I discovered I was using a.lineTo(x,y); a lot, where a is the canvas context. Closure can’t rename a.lineTo, so I needed to give it something it could rename:

function lineTo(x,y) {
  a.lineTo(x,y);
}

Again, there’s some overhead involved in setting this up, but with enough use of a.lineTo it’s well worth it.

I had to work round a bug with the compiler here. When it gets to the definition of lineTo it makes the assumption that inlining it will save space. So every occurrence of lineTo(x,y) in the code is replace by a.lineTo(x,y), and it removes the lineTo definition. To stop it doing this, I had to add the following line:

window['lineTo'] = lineTo;

This line forces the compiler to keep the lineTo definition, and when compiled produces window.x=x;. This is good and bad news – lineTo is now kept (in minified form) but there’s a useless line added. The way I got round that was to add a post-compilation step to my Makefile:

    sed -i 's/window\.[a-zA-Z]*=.;//g' kave-min.js

(Yes, I learned sed for this. That’s dedication.)

2. Optimising the code

Some code optimisations just make the code size smaller, full stop. One is true and false. In a lot of cases, 1 and 0 will do just fine, at a quarter of the size.

Code organisation matters too. You may be used to writing nice modular code with no global variables and loose coupling between each module. All those good intentions need to be completely suppressed. Make everything a global unless it absolutely has to be local. Everything should know about everything else. Couple wherever possible.

An example of coupling comes in the rendering stage of the game loop. The context fillstyle is set to white. After this, the snowball is rendered, followed by the snow. Then the fillstyle is set to blue, and the icicles and walls are rendered. The order of these rendering functions is tightly coupled in terms of order of execution. Trying to render the icicles before the snow would make the icicles white, unless we changed the fillstyle to blue then back to white for the snow.

3. Changing the behaviour

I said that the first two techniques “defactored” the code, leaving the behaviour unchanged. Sometimes the current behaviour is essentially complex, and needs to be simplified to reduce its code size. The simplest solution is to just leave out functionality, but sometimes that’s a compromise too far. Following are some less drastic options.

Here’s a really simple example. I picked a nice blue for the icicles with the following line:

a.fillStyle = "rgb(190,230,255)";

Unfortunately, the syntax for writing arbitrary rgb colours is relatively verbose. Much shorter are the colour names, like “yellow” or “blue”. “blue” was the wrong colour, but using Doug Crockford’s nice CSS colour chart I was able to find “lightblue”, which was pretty close to the original colour, but saved me 7 valuable bytes over “rgb(190,230,255)”.

The snow falls diagonally, but with a little bit of added randomness. I wanted it to follow a sinusoidal curve as it fell, but the additional code to enable this was too long, so I cut it. The snow’s not as natural as I’d like, but I had to compromise.

The collision detection algorithm’s pretty simple. I used the context.isPointInPath(x,y) method, passing the front middle point of the snowball. I could then call the detectCollision() function every time I drew a new path that I wanted to check (i.e. the walls and the icicles). It would be nice to check the top, front and bottom points of the snowball, but that would have been too much code.

Don’t try this at home

Coding is usually about weighing things up: readability/maintainability, execution speed, memory use, code size, etc. When you’re doing a JS1k submission, the only one of these that matters is code size (well, execution speed can matter as well, but only as a secondary concern). The important thing to remember though, is that this kind of optimisation is wrong, just plain wrong. It’s almost never a good idea to sacrifice maintainability for code size. If it were, we’d all be writing Perl. I’ll leave you with that thought.

Categories: Programming Tags: ,
  1. December 12th, 2010 at 14:55 | #1

    Nick, I’ve also explained some of the byte-saving techniques I used in the creation of Organ1k, my previous JS1k entry. See both the article and JavaScript source comments:

    http://benalman.com/news/2010/08/organ1k-js1k-contest-entry/ https://github.com/cowboy/js1k-organ1k/blob/master/organ1k.js

  2. December 12th, 2010 at 16:52 | #2

    @”Cowboy” Ben Alman Thanks for that. I’ve seen some other write-ups of other JS1k submissions too – maybe I’ll make a post of links, as there’s a lot of good information out there. Loving your Organ1k submission btw :)

  3. August 2nd, 2014 at 04:55 | #3

    Pistons jerseys factory. cheap jerseys at wholesale price and 100% quality guarantee.

  4. June 14th, 2015 at 03:18 | #4

    How to transfer my blog from blogspot to paid hosting?

Comments are closed.