Implementing DHTML Chuckie Egg


1. Sourcing Material

When I started working on DHTML Chuckie Egg, I didn't have the original source code, so I had to sample what I could from the emulated version of the game. I had the original BBC Model B binary on cassette but had transferred it first onto a DFS 5 1/4 inch disk and then onto a 3 1/2 inch Acorn Archimedes disk. Finally, I used the Redsquirrel Acorn Archimedes Emulator to transfer the binary to HostFS mounted as a directory under NTFS.

Using this emulated version, I was able to sample audio (using the audio loop-back on my sound card), grab screen layouts and graphics (using PrtScn / mspaint.exe provided with windows) and compute the distances jumped by the farmer / travelled by the birds (and Mother Duck) in a given time. The emulated version served as my reference and I tried to make the DHTML version as faithful to the reference as possible.

One interesting problem was to determine how the difficulty of the original game progressed. Specifically, I couldn't reach as far as level 32 so I had no idea what happened when you get there. Luckily, I was able to use my port of Chuckie Egg for Windows to fast forward (cheat) to identify the following difficulty progression:

2. Empirical Approach Problems

Trying to use an empirical approach to accurately recreate a game is both tedious and time consuming. Despite lots of perseverance there are still two remaining inaccuracies of the DHTML version:

The first problem has its roots in random number generation. It is impossible to decode how the original Chuckie Egg generated its pseudo random number sequence. Even if I had the algorithm it would be impossible to determine the order in which random numbers were calculated which would affect the sequence observed. The best I could do here was to just use Javascript's built-in random number generator to make decisions over whether a bird should climb up, climb down, walk left or walk right. As it transpires, this doesn't detract too much from playing the game. There were certain guides through the levels I had learned from the original Chuckie Egg which depended on the position of the birds at certain times - this niggling problem was one of the issues that motivated me to do a second remake using a simulated approach. Learn more...

The second problem is a collision detection/response problem. The original collision was never pixel perfect and I have had to guess at it. Despite this, the remake is closer to the original than any other PC remake I have seen yet.

3. Web Technologies

DHTML (Dynamic HyperText Markup Language) is a general term used to describe HTML documents that are modified on the fly by adding new content, removing content or modifying existing content. Although I use the term to describe the technology underpinning this Chuckie Egg remake, in actual fact I have only really used dynamic CSS (Cascading Style Sheets). As I'll go into later, this is a much more efficient approach to updating pages and so it is more applicable to real-time browser based games.

4. Sprite Engine

The original Chuckie Egg (for the BBC Model B) used XOR to write and erase sprites without redrawing the entire screen each frame. I had to construct something using DHTML capable of efficiently writing and erasing sprites. Luckily, the web browser makes this super easy - I used Javascript to change the CSS x,y coordinates of an absolutely positioned HTML image element over time (respecting CSS z-order to ensure everything was drawn correctly). Chuckie Egg didn't require anything more than this - the browser takes care of all redrawing with the added benefit that there is no discernable flickering (unlike the original BBC Model B version). Transparency is handled through the use of .gif/.png image formats.

Where performance was critical it was necessary to add sprites (<IMG> elements) to the HTML document upfront and use javascript/CSS to control element visibility over time. This is because it is much more efficient to change the style of an existing element than it is to add/remove elements from the DOM.

I wanted a coordinate system relative to 0,0 (the upper left pixel of the BBC Model B Screen) and achieved this by wrappering all the HTML elements in an outer div section and setting its CSS position to 'relative'. This allowed me to place the screen anywhere on a web page with ease. I have used the same trick to make the positioning the content below the score bar easier.

Scaling the screen up to make the visible area larger was something I decided to delegate to the browser - but it could easily be added to the code. The game is architected in such a way that the final CSS x,y positions are set in a few key places where scaling could easily be applied.

5. Sprite Animation

I had to find a way to animate characters efficiently. It turned out that the cheapest and simplest way to achieve this was to pack all animation key-frames for a character into a single image and use Javascript/CSS to modify the clip area (indicated by the black rectangle in the image below) whilst simultaneously shifting the position of the HTML image element left or right so that the clipped area was aligned over the characters desired position on screen.

Mother Duck, the farmer and the hens were all configured in this way. Since walking, climbing, jumping and eating animations were all combined into a single bitmap, a separate index list was used to select the appropriate key-frames for when the character was performing a specific action.

Although the image content is clipped, the browser is aware of the original image size with the result that it can sometimes turn scroll bars on/off to accomodate the content. This is only really a problem when the browser window is small, but it can be fixed by giving the enclosing <div> element the a CSS overflow property of 'hidden'.

<div id="characters" style="position:absolute;left:0px;top:0px;width:320px;height:224px;overflow:hidden;">

6. Fonts

The BBC Model B version of Chuckie Egg uses the built-in mode 2 font to display text. To capture the full mode 2 character set, I wrote this little program in BBC Basic and ran it under the BBC emulator - screen grabbing the result.

	10 MODE 2
	20 FOR A%=32 TO 128 STEP 1
	30     PRINT CHR$(A%);
	40 NEXT

It was necessary to grab images of all the characters to correctly support name entry on the high score table and to display the user's re-defined key selections. The font system uses the same trick as the sprite animation system to address a given portion of a font page sprite i.e. it modified the x,y coordinates and clip rectangle of the HTML image element to position the character on screen.

I had to manually build up the font page sprite for the numerals on the score bar by playing the game and screen grabbing until I had a complete set.

7. Level Layouts

To save a bit of effort, I borrowed a text file describing the layouts of the levels from Mike Elson's Chuckie Egg remake and fixed up the indexing to work within the Javascript framework. The simplest way to layout the level was to use this information to populate a 20x28 grid of HTML IMG elements contained within a HTML DIV and positioned using CSS. I chose this method rather than populating a HTML table because of browser compatibility issues with CSS.

I wrote this DHTML based level editor to allow users to define their own layouts and to provide the alternative layouts defined by popular hacks and upgrades released shortly after the orginal BBC model B version. I have provided pre-defined layouts for Chuckie Egg - The Upgrade, Chuckee, Chuckie Egg 2 and Chuckie Egg 4.

8. Frame Rate Timing

In the original version of Chuckie Egg, it takes the farmer approximately 4.5 seconds to travel a distance of 150 pixels. So it takes him 0.03 seconds to travel a distance of one pixel. Now the farmer always moves one pixel per game update, so the frame rate must be set to 1/0.03 = 33Hz. This equates to a frame time of 30 milliseconds.

There are several factors to consider when managing frame rate in Javascript:

The difficulty of achieving a stable/smooth frame rate is nicely described in this article by John Resig on the accuracy of the Javascript getTime() function. His key finding is that some web browsers only update their internal getTime() representations every 15 milliseconds. Any test that takes less than 15ms will always round down to 0ms. I also found this to be the case with timer call-backs fired as a result of windows.setTimeOut(). This makes it difficult to use the technique described on Larry Bank's programmer's corner website to achieve accurate low CPU overhead frame rates.

One issue that confused me for a while was that the getTime() method of the Date object always returns the time of object construction, not the current time. This led me to write this function to sample the time:

	function Time()
		var date = new Date;
		var newTime = date.getTime();	
		delete date;
		date = null;
		return newTime;

I also found that spinning in a loop whilst sampling the current time in this way resulted in more work for the garbage collector so I attempted to delete the date object manually after its use. However, I was still concerned that this would ultimately lead to inconsistent frame rates.

One scheme I tried was to count the number of while loop iterations that completed within a second to estimate how many I would need in order to spin for one millisecond. An error of 15ms in 500ms works out at 3% accuracy, which could be tolerable. The idea was to use windows.setTimeout() to fire a call-back every 15ms (thus being friendly in a cooperative multitasking environment) and then spin for the remaining time. Unfortunately, this seems to have the nasty side effect of interfering with garbage collection and task scheduling in addition to having a large CPU footprint. Furthermore, Internet Explorer pops up a dialog box for any script running for more than about half a second - thwarting attempts to do timing.

Stop this script running?, A script of this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive.

The solution I eventually adopted was to just use 'setTimeout()/clearTimeout()', requesting a 30ms time out. Since the timers are rounded to be multiples of 15ms, the call-back was reliably fired every 30-31ms. Clearing the existing timeout and issuing the next windows.setTimeout() immediately on entering the call-back function ensured that variable frame-time due to garbage collection, game events, audio sample caching etc. was not a problem. If a frame took more than 30ms, the game would just frame-out. Having tried lots of schemes, this seems the most reliable approach across browsers. Below is an outline of how this mechanism works in DHTML Chuckie Egg.

	var nmsec = 30; // Number of milliseconds that must elapse between frames.

	// Initiate first timer callback.
	var nTimerId = window.setTimeout("GUIUpdate()", nmsec);

	function GUIUpdate()
		// Quickly issue next timer callback
		nTimerId = window.setTimeout("GUIUpdate()", nmsec);

		// Update game frame (should take significantly less than nmsec milliseconds)
		// ...

The end result is fairly successful - the browser's CPU utilisation doesn't even show up in Task Manager.

9. Optimisation

There are several standard tricks available to speed up the execution of Javascript programs. Here are the ones I used in DHTML Chuckie Egg.

10. Audio

I originally thought that the sound effects would be too difficult to implement but I have managed to get good results from a range of web browsers. Luckily audio in Chuckie Egg is very simple - there are only eight unique sounds.

In summary, I needed to support one-shot sounds, looping sounds and sounds that change in pitch.

Browser support for audio is very simplistic - there are compatibility issues and considerable latencies involved in triggering a sound. In terms of a cross-platform solution, the only functionality you have is play, stop, pause, rewind and loop - so changing pitch was out of the question. I side-stepped this issue by trying to sample the longest falls and jumps I could from the emulated version in order to treat them as one-shot sounds. I was able to stop the one-shot sounds when the game demanded such as on collision with a platform. I found places in the original game that would give me nice long falls and jumps in order to get long enough audio samples to cover all possible outcomes for the farmer.

Cross-browser/platform audio compatibility was really difficult to get working adequately. Every platform has its quirks so I was forced to resort to browser detection and 'lowest common denominator' as part of the solution. I didn't use <BGSOUND> because that was only supported properly in Internet Explorer. <OBJECT> worked differently in different browsers too - some supporting the classid attribute others didn't. In the end I settled with <EMBED>. This was the tag I had the most success with. I did have a problem that stumped me for a while though - if I completed the <EMBED> tag as per correct XML syntax, the looping would not work e.g.

	<EMBED src="sounds/tune.wav" loop="true"/>
wouldn't work but...
	<EMBED src="sounds/tune.wav" loop="true">
worked fine. In the end I did this:
	<EMBED id="grainSound" src="sounds/grain.wav" hidden="true" width="0" height="0" autostart="false" loop="0"></EMBED>
All <EMBED> elements were added dynamically after successful detection of browser audio support. Failure to do this results in script errors and that nasty 'Install Missing Plugins' toolbar being displayed.

One particularly annoying problem with <EMBED> was that dynamically inserting it into a page and then calling document.getElementById() would return null. I guessed that this was due to the sample not having been loaded, so I added an onload() call-back and some code to validate that all samples had been loaded. It turned out that I didn't need any validation code. Simply adding the onload() callback with a dummy function was sufficient.

	<EMBED id="grainSound" src="sounds/grain.wav" hidden="true" width="0" height="0" autostart="false" onload="onAudioLoad();" loop="0"></EMBED>
	function onAudioLoad()
		// Empty

I made the mistake of trying to hide the audio plug-in control toolbar by setting the 'hidden' attribute of the EMBED tag to true. However, this mutes the sound as well as making it invisible with some media plug-ins - this wasted a day's worth of work. To work around the problem I wrapped the EMBED tag in a div with its visibility style set to hidden.

	<div id="audioSamples" style="visibility: hidden;">
		<embed id="grainSound" src="sounds/grain.wav" width="0" height="0" autostart="false" loop="0"></embed>

When you embed sound in a web page, it is important to check that the required plug-in is installed on the user's machine. This cannot be taken for granted. Opera and Internet Explorer have audio support out of the box but Chrome, Netscape and Firefox rely on plug-ins to provide audio support. There are three things to consider when getting a plug-in to work.

The key to understanding plug-ins is the MIME type. Every type of media has a MIME type associated with it such as "audio/wav", "audio/mpeg", "application/x-mplayer2" and "text/plain". Usually, your web browser can determine the MIME type from the file extension or by parsing the first section of the media file/data. However, you can manually tell the browser what the MIME type is - which can prove to be very useful indeed. For example, without manually specifying the MIME type, Chrome wouldn't prompt that a plug-in was needed.

	<embed id="grainSound" src="sounds/grain.wav" type="audio/wav" width="0" height="0" autostart="false" loop="0"></embed>

The smallest, most widely supported audio format is .wav - however Firefox doesn't provide scriptable support for this media type out of the box. In fact, for the most common formats found on the web (like mp3, midi, mpg, wav, etc.) you will need to install and configure a plugin. The plug-in finder service built into firefox and other Mozilla based browsers uses the MIME type to help it locate an appropriate plug-in to handle the media. Typing 'about:plugins' (or 'opera:plugins' in Opera) into the address field of a Mozilla based browser will display a table of currently installed plug-ins and the MIME types supported by them.

You may notice that there is a plugin resident in the install of all Windows Firefox/Chrome/Netscape/Mozilla based browsers called the 'Windows Media Player Plug-in Dynamic Link Library'. This does provide .wav playback - it supports .asf, .asx, .wm, .wma, .wax, .wmv, .wvx and using the audio/x-ms-wma MIME type it is possible to get latency free .wav playback. However, contrary to expectations, the playback is not scriptable from Javascript. By default, the plug-in finder service recommends installing Apple QuickTime - DON'T. Whilst this does support "audio/wav" there are just too many problems with it to be useful. Here is the list of problems I experienced using it.

I had already worked around issue five using a hidden <DIV> section to wrapper the element. I solved issue two by writing the code to always work with QuickTime's case preference. I partially solved issue four by capturing longer versions of the walk/climb/applause sounds. Since the repeat is now less often, the pause on repeat is less noticeable. This hasn't made the problem go away but it has improved it.

In order to prevent the Internet Explorer version of DHTML Chuckie Egg from suffering audio latency (when QuickTime was installed), I wanted to use a different MIME type - one which QuickTime doesn't override. The following MIME types seem capable of playing wav audio:

However, Internet Explorer ignored the MIME type specified explicitly in the <EMBED> tag and defaulted to 'audio/wav' which QuickTime had control over. I tried to remove the .wav file extension from the files in the hope this would fix the problem. It did but with the rather odd side-effect of having to call play() twice before sounds became audible. There were two remaining solutions:

In the end I generated the following HTML to embed audio in Internet Explorer (rather than using the <EMBED> tag) to guarantee the use of a low-latency audio plug-in:

	<object id="updownSound" name="eggSound" height="0" width="0" classid="clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95">
		<param name="AutoStart" value="0"/>
		<param name="FileName" value="sounds/updown.wav"/>
		<param name="Loop" value="1"/>

The classId specified above is for Windows Media Player 6.4. This version is sufficient because Chuckie Egg only needs basic scripting but the classIds for Windows Media Player 7 upwards could be used too. One caveat with specifying the classid manually is that browsers prompt the user for action because the page is trying to run an ActiveX control and this could represent a security risk. Consequently, it is better to omit the classid completely - it turns out that Internet Explorer doesn't actually need it as long as the 'audio/x-ms-wma' MIME type is specified instead.

	<object id="eggSound" height="0" width="0" name="eggSound" data="sounds/egg.wav" type="audio/x-ms-wma">
		<param name="src" value="sounds/egg.wav">
		<param name="autoStart" value="0">
		<param name="loop" value="1">

I would highly recommend using a different media plug-in to provide "audio/wav" support to Firefox. Microsoft have released the Windows Media Player Plugin for Firefox. This adds low-latency "audio/wav" playback support to all Mozilla based browsers installed on your system (including Chrome and Opera). If you install this plug-in after QuickTime, all the MIME Types that you would still want QuickTime to support (such as .mov) will still work. One problem is that looping doesn't seem to work. Since I have lengthened the looped samples to overcome audio latency with QuickTime, this isn't so noticeable. Another problem is that the scripting interface is different. Rather than:


It is necessary to use:;

In order to detect whether or not this plug-in is in use (and correct the play/stop commands appropriately), I look to see if the "application/x-ms-wmp" MIME type is supported by an enabled plug-in. If it is, I use it instead of "audio/wav". This works fine on Firefox/Chrome/Opera and "audio/wav"/"audio/mpeg" are used as fallbacks for other browsers.

The installer for the Windows Media Player Plugin for Firefox, does not correctly detect Opera. It is necessary to copy the plug-in manually into Opera's plug-in folder e.g. from C:\Program Files\Opera\Program\Plugins\np-mswmp.dll to C:\Program Files\Opera\Program\Plugins. Another problem with Opera is that the <EMBED> element results in an unscriptable audio object. It is necessary to use the following object element instead (i.e. with the "application/x-ms-wmp" MIME type).

	<object id="eggSound" height="0" width="0" name="eggSound" data="sounds/egg.wav" type="application/x-ms-wmp">
		<param name="src" value="sounds/egg.wav">
		<param name="autoStart" value="0">
		<param name="loop" value="1">

As a fallback for Opera, I provided support for the WHATWG Audio interface. Opera have implemented this from the HTML5 draft specification, but it has some problems. Triggering an audio sample too soon after stopping that same sample results in no audio playback. You get about 70% of the audio experience you should for DHTML Chuckie egg.

As an extra level of protection, I wrapped calls to the audio scripting interface with try/catch blocks, ignoring the exception if it is raised.

	try { this.cachedSamples[sound].Play(); } catch(e) { } // Mozilla is case sensitive here 
	try { this.cachedSamples[sound].Stop(); } catch(e) { } // Mozilla is case sensitive here 
	try { this.cachedSamples[sound].Rewind(); } catch(e) { } // Mozilla is case sensitive here 

11. Character Physics

In order to approximate the character physics as closely as possible, I used purely integer arithmetic (16 bit). This is how the BBC Model B version would have worked and I think it has helped me gain a more accurate simulation. In order to replicate the behaviour, I measured the distances and times involved in a jump, fall, climb and walk and used Newton's equations of motion (together with the 16bit fixed point arithmetic) to move the farmer about. The results quickly resembled the original version of the game. I repeated the same approach for the Mother Duck as discussed below.

One nice side-effect of working this way is that the code should be more portable, especially for small mobile devices which lack floating point support or provide it at a cost.

12. Mother Duck Behaviour

My initial attempt at replicating the behaviour of Mother Duck involved using Hooke's law (F = -kx) and Newton's equations of motion (F = ma, v = u + at and v = d/t) together with a damping constant whilst ensuring the implementation used fixed point arithmetic. Despite my best efforts, I could not replicate the original behaviour.

Animated Mother Duck

I had been fooled into thinking that the force applied to Mother Duck was proportional to the distance from the farmer (i.e. a spring). But, I was able to establish that the acceleration (and hence force) applied was constant by carefully screen grabbing frames of animation and measuring how far Mother Duck moved in a frame. This is illustrated in the following table where horizontal displacements have been derived from the animation above. As you can see, the acceleration always comes out as -1.

Horizontal Displacement (pixels)FrameVelocity (pixels/frame)Acceleration (pixels/frame2)
Rate of acceleration of Mother Duck derived from analysis of displacement over time.

This meant that the BBC model B implementation was considerably simpler than I had originally thought. In the end, I assumed a time delta of one between updates and did away with fixed point arithmetic entirely resulting in a pixel perfect recreation of the original BBC Model B behaviour.

13. Farmer Behaviour

The table below shows the measured displacement (in pixels) for each frame of the farmer's jump animation. I used this to derive his velocity and acceleration which appears to change to -1 every fourth frame. This indicates that the acceleration is really -0.25 pixels/frame2 and due to integer arithmetic, we only see whole numbers.

Jumping farmer animation
Horizontal Displacement (pixels)FrameVelocity (pixels/frame)Acceleration (pixels/frame2)
Rate of acceleration of farmer derived from analysis of displacement over time.

To replicate this behaviour, the velocity was always multiplied by four and then scaled back down again when updating the displacement each frame. Taking into account frame rate, the acceleration due to gravity applied to the farmer works out to be approximately 8.3 pixels/s2.

I did a similar analysis for when the farmer walks off a platform which revealed that he continues to walk sideways for two frames after entering a falling state.

Farmer walking off platform animation

Similarly, I was able to replicate the correct collision behaviour of the original BBC Model B version by carefully analysing the complex set collisions that take place on level one.

Complex farmer collision animation

From this animation it is possible to work out where the centre of the farmer is and how big his collision rectangle is. It turns out that his ankle is used as the centre and he is allowed two pixels worth of overlap with any wall before a collision is detected.

14. Keyboard Input

I used the onkeydown and onkeyup events on the HTML <BODY> element to capture and key presses. The handlers themselves just toggle a Boolean to say whether a key is currently pressed and the game polls this information each frame.

Some browsers reserve certain key presses for operations like 'find'. When this is the case, alternative keys must be selected using the re-define keys option in the game. So, just like the original BBC Model B version states, "some keyboard combinations may not be possible due to keyboard clashes".

One particularly annoying keyboard clash is the backspace key. Backspace usually instructs the web browser to navigate back a page, but this isn't necessarily what we want when entering a name into the high score table i.e. the player would expect the backspace key to behave like the delete key. Luckily it is possible to intercept the backspace key using Javascript event handlers and instruct the browser not to navigate backwards. The following code implements a solution - I found it on a forum.

	// Disable backspace keypress
	if (typeof window.event != 'undefined')
		document.onkeydown = function()
			if (event.srcElement.tagName.toUpperCase() != 'INPUT')
			return (event.keyCode != 8);
		document.onkeypress = function(e)
			if ( != 'INPUT')
			return (e.keyCode != 8);

The basic idea is to trap backspace and return false from the keyboard handler to stop the key press being propagated onto the browser. It is important that it is the document's keyboard handler and not the body elements handler. One small gotcha is that on some browsers this causes the game to stutter badly when more than two keys are pressed together. Consequently, I only trap the backspace key during high score name entry.

15. Game Size

The original Chuckie Egg for BBC Model B weighed in at just 9.75kb (9,984 bytes). I wanted to make the DHTML version comparable. If it wasn't for having to bake-out looped samples to work around issues with audio latency, I may have come close. The images contribute 4714 bytes and in an uncompressed form, the Javascript is 101kb (102,457 bytes).

DHTML Chuckie Egg is written as a collection of Javascript(.js) files, one per class e.g. farmer.js, hens.js, gui.js etc. I used Saltstorm's ECMAScript Pre-processor to compress and combine all the Javascript into a single file. A pre-requisite for lots of Javascript compressors is that each line is terminated by a semi-colon. I used Javascript Lint by Matthias Miller to check over the code before submitting it to be compressor.

The final compressed version weighed in at 53.5 KB (54,877 bytes). I was disappointed by this because looking at the output, it hadn't compressed class member variable names - only global and locally scoped variables. DHTML Chuckie Egg is written in an objected oriented way and I have avoided global variables for performance reasons, consequently I got hardly any compression from variable renaming. Dean Edward's excellent Javascript compression site provides comparisons between some of the best compressors but none of them seemed to do the trick. The final step is to GZIP the Javascript - there are several options here. Either your host needs to provide compressed HTTP support or you need a self decompressing Javascript program. The article here discusses several options. Zipping up the file brings the size down to 12.3kb (12,631 bytes).

16. Storage

For security reasons, it is forbidden for Javascript to access your hard disk. So in order to store data such as a saved game, high score table or user defined level layout we have to resort to using cookies. Cookies are associated with a particular server/client and persist even after you close your web browser and turn off your computer. The amount of data that can be stored using cookies varies between web browsers but is generally quite small for security purposes.

Typically, a cookie can be one name-value pair of 4Kb, or it can be up to 20 name-value pairs that have a total size of 4Kb (the name and value of the cookie count towards this limit). Consequently, it is good practice to combine values into a single cookie (using some form of delimiter) rather than using one cookie per value.

DHTML Chuckie Egg uses cookies to store user defined level layouts generated by the DHTML Chuckie Egg Level Editor and high scores. In order to fit all the information within 4kb, it is necessary to resort to a compression strategy. RLE (run-length encoding) is used to achieve this together with a scheme for packing RLE value-count pairs into readable ASCII values. A single cookie name-value pair called 'levels' is used to store the encoded data for all eight layouts and it consumes around 1.6Kbs. Learn more...