About Me

Curriculum Vitae

A brief list of my current skill set


Some Days I Wish For an Async String.replace
Monday, 19th November 2012, 12:59

MinnaHTML to the rescue!

Object Oriented Programming for Javascript Dummies - Part 2
Tuesday, 6th November 2012, 15:33

OOP it up to the OOPballs

Object Oriented Programming for Javascript Dummies - Part 1
Tuesday, 30th October 2012, 16:01

Not using OOP? Are you mad?

Strange Windows Errors and a Bowser in My Event Log
Wednesday, 24th October 2012, 11:10

It's like my own DailyWTF round-up!

Why Do People Come Here?
Monday, 15th October 2012, 15:47

They come to look at porn!

Idiot thinks Raspberry Pi Unsuitable for Education
Tuesday, 2nd October 2012, 15:24

Dumbest thing I've read since...

Upgrading to PostgreSQL 9.2 on CentOS 5
Tuesday, 25th September 2012, 14:52

It's easy as PI!

Fare Ye Well Work Email You Have Served Me Well
Monday, 17th September 2012, 14:36

Cause of death too much spam

Forest Racer - A HTML5 Game in Under 13K
Tuesday, 11th September 2012, 20:46

Including all assets, but only when zipped

Entering a 13k HTML5 Game Competition
Tuesday, 4th September 2012, 16:31

I'm so tempted to have a go at this

Faster Loops and Faster Iterations in Node.js and V8
Wednesday, 29th August 2012, 13:16

Is Object.keys faster than For...In?

And the Fastworks.js framework is Born!
Wednesday, 22nd August 2012, 16:23

Well I'm excited, even if you aren't

Libxmljs Update on CentOS 3.8 throws an SELinux Wobbley Fit
Monday, 20th August 2012, 15:40

The right way to fix this sort of issue

TV Land Doesn't Understand Technology
Friday, 17th August 2012, 17:09

Or maybe it does and thinks we don't?

Yet More Benchmarking - Function Chains vs Object Chains
Wednesday, 15th August 2012, 13:34

Working towards a faster Node.js framework

Projects and Sillyness

MAME Cabinet Diary

How I built my own arcade cabinet

Loading Screen Simulator

I don't miss the ZX Spectrum, I still use it!

The Little Guy Chat Room

It's a Pitfall inspired chat room


A fully featured MP3 player what I wrote

GP Space Invaders

My first little emulator

GP32 Development Page

Some info and links about this cute little handheld

Disney Nasties

Uncensored images, you must be 18 to view them

Diary of a Hamster

Learn about how hamsters think, first hand


Time Calculator

A simple little online utility for working out how many hours to bill a client

A Few Links

Forest Racer - A HTML5 Game in Under 13K
Tuesday, 11th September 2012, 20:46

So, earlier today I finished and submitted my entry to the Js13kGames competition. If you remember in my previous entry, I said I planned to make a Deatchchase style racing game, and in the end with barely half a kilobyte to spare, I managed to implement everything I wanted.

With more time, it would have been great to play with Mozilla's Audio Data API or whatever Chrome has as an alternative or equivalent. But I only found out about the competition last week, and with things being so busy I only had a few days to throw at it.

Overall I'm quite pleased with how it turned out, with more time and space I'd be able to do a lot more, but every single thing I wanted to get in, I managed to.

Thoughts on HTML5

Well, this really does remind me of the old IE5/IE6 and Netscape days, where so many things don't work properly in one browser yet do in another. It's frustrating that some things don't do what you expect anywhere, and others are very broken on some.

Like there is no way to manipulate directly the pixels of a Canvas area, the only way to read a pixel is to getImageData to pull a 1x1 pixel, which results in a completely unnecessary overhead. It's as if the people designing these APIs were smaller than a foetus in the golden age of home computing. Just as ridiculous is how you can use getImageData/createImageData to create an image buffer that you can manipulate directly, and then provide no simple way of setting/reading those pixels without accessing the 8 bit RGBA values.

I don't mind this too much, it's a very old school way of doing things that gives you the illusion of "hitting the hardware directly" without actually really doing that. But the advantage of doing that is that... you are hitting it directly to save time.

Another stupidism is with the putImageData method, which makes the alpha channel quite useless when it comes to overlaying sprites with it. The method should really have an option or sister method to merge the RGB data based on the alpha channel and pre-existing values on the canvas, rather than just overwriting them. To work around it I had to do the following long winded method:

  • Create a new Canvas element
  • Use putImageData to copy the image to the canvas
  • Use toDataURL to copy the canvas to an image encoded URL
  • Then create a new Image() element and assign that URL as the src

Only then could I write my sprites with transparencies to the main canvas.

Support for the <audio> tag, in particular currentTime property proved very sketchy. In Chrome the whole thing works most of the time, but sometimes you have to refresh the page to get it going properly, and you also have to put setting that property in a try/catch block otherwise it sometimes randomly throws an error.

Firefox doesn't seem to handle that property properly at all, you can set it to 0 as much as you like, but most of the time Firefox ignores your plee and just finishes the sample and then does nothing for ages despite requests to play() it.

Some Tricks I Used

One technique I used which may be of interest, relates to frame rate. I set a target framerate of 30 per second, and use some code to try achieving this as accurately as possible. The key is ONLY to rely on setTimeout() and use a bit of maths.

Firstly, take your 1000 milliseconds and divide that by your frame rate, so in this case we get roughly 33ms between frames. Then what you do is at the start of your function which is called every frame, you set a variable to the current time with the Date() object. At the end of your frame update, you calculate how long the frame took to generate, and then subtract that from the next setTimeout value.

That way, if a frame takes 5ms to calculate, the next update will be called in 28ms (33ms - 5ms), simple!

ForestRacer.prototype.processFrame = function() {
var dtStart = new Date();
var self = this;


var dtEnd = new Date();
var msDiff = dtEnd - dtStart;
var msNextFrame = (1000 / 30) - msDiff;
if (msNextFrame < 1)
msNextFrame = 1;

this.timeout = setTimeout(function() {
}, msNextFrame);

The audio is broken up into two files, one is a crash sound, the other is a revving engine. I keep a counter that causes the engine sound to loop faster depending on the speed, which gives you the illusion that the engine is going faster when really it isn't.

Keys are handled using an onkeydown/onkeyup routine, that sets properties of an object to true or false, whenever they match an object that lists keys with their keycodes. This should allow easy redefining of them, if I'd had room to add it.

    this.keys = { left: 78, right: 77, up: 65, down: 90, start: 83, debug: 89 };
this.keysdown = {};

var self = this;

window.onkeydown = function(event) {
for (key in self.keys)
if (event.keyCode == self.keys[key])
self.keysdown[key] = true;

window.onkeyup = function(event) {
for (key in self.keys)
if (event.keyCode == self.keys[key])
self.keysdown[key] = false;

So you can see, this lets us check at any point with something like this

    if (this.keysdown.right)

The added bonus is (for example) you could also then set the this.keysdown.right to false and that key would not be triggered again until either a key repeat or being repressed.

One last thing I did, was to have one single setTimeout function which performs all the generic requirements for each frame, and then just calls a member property which is set to whatever current game mode function is required. That way to swap game modes, all you have to do is re-assign the function to another member property.


You can download the source code and game at Github, or you can just click and play it here, best use Chrome for better sound though.


Add Your Own Comment