Illyriad: HTML5 WebGL Preview 2

We thought we’d up the game on the last demo and show a bit of lighting and animation.

A high level flour mill from Illyriad seemed an obvious choice!

Again this is purely HTML5 using the WebGL canvas and JavaScript; no plugins were used. We use mrdoob’s excellent Three.js library:

[youtube=https://www.youtube.com/watch?v=0bZmt3yQzmE]

We’ve included the full browser window in this recording to show that it is in fact running in a Chrome browser window. While it happily runs at 1080p, we’ve had to record at a much lower resolution as the screen capture utility slows everything down… Alas.

Illyriad: HTML5 WebGL Preview

We’ve done a bit of bloging about the “now”, but what about the future?

Well here at Illyriad we’ve been experimenting with WebGL and I can tell you we are impressed with how its shaping up.

Here’s a little taster of what we’ve been trying:

[youtube=https://www.youtube.com/watch?v=F22IvhUtrmM]

Its slightly shaky – but that’s my mouse movement not any lag from WebGL. Looks like I might never be a brain surgeon… haha

HTML5 Canvas: Creating a screenshot from multiple canvases in JavaScript

Programmatically creating screenshots from JavaScript of your canvas element is fairly straightforward; but how do you do it when your game or scene is composed of multiple overlaid canvases?

This example makes use of the canvas caching and it links into the animation loop for starting and stopping – your mileage may vary.

For simplicity, lets say we have 4 canvas elements in use, all of the same size (500×500) used in the following order:

  1. A far background: canvasFar
  2. A near background for a touch of Parallax scrolling: canvasNear
  3. A layer for player characters and other objects: canvasSprites
  4. A layer for special effects: canvasEffects

[code lang=”javascript”]
function TakeScreenshot() {
StopAnimating(); // Stop the animation loop

var bufferScreenshot = CreateCanvasCached("Screenshot");
bufferScreenshot.height = 500; bufferScreenshot.width = 500;
var contextScreenshot = bufferScreenshot.getContext("2d");
// Draw the layers in order
contextScreenshot.drawImage(
document.getElementById("canvasFar"), 0, 0, 500, 500);
contextScreenshot.drawImage(
document.getElementById("canvasNear"), 0, 0, 500, 500);
contextScreenshot.drawImage(
document.getElementById("canvasSprites"), 0, 0, 500, 500);
contextScreenshot.drawImage(
document.getElementById("canvasEffects"), 0, 0, 500, 500);

// Save to a data URL as a jpeg quality 9
var imgUrl = bufferScreenshot.toDataURL("image/jpeg", .9);

StartAnimating(); // Restart the animation loop
return imgUrl;
}
[/code]

Now calling TakeScreenshot() will return a data URL that contains the image – with this you can:

  • Set the src of an image element or css background
  • Open it in a new window to display it as an image (unfortunately you can’t set the content-disposition or filename of a data URL for automatic download)
  • Send it to your sever to base64 decode and create an image file which you can then to popup a download or embed in a page which can then be shared, tweeted, posted to Google+ or Facebook.
  • Use it as source image for more canvas fun or a WebGl texture

UPDATE: There is a toBlob() method on Canvas that allows the saving of generated files on the client-side which is coming in Chrome 14, hopefully other browsers will follow suit.

This should all work fine.  However if you are using images from different domains or a multiple sub-domains – either by using a CDN or for paralleling requests across domain names you will receive a:

SECURITY_ERR: DOM Exception 18

This is a security protection to ensure your code isn’t grabbing images it shouldn’t be, and sending them back to your server. I’ll post about how to work with this in a future post.

(Fix) Memory Leaks: Animating HTML5 Canvas

If you create canvas elements dynamically either for off-screen composition or sprite manipulation, when this is in an animation loop running hundreds of times a second it can start to leak memory.

I’m not entirely sure why this is; but it’s quite widespread in many browsers including Webkit. Perhaps the animation causes the browser to register as continually active which prevents the garbage collector from running – as it will clean it up if you stop the animation and change DOM elements e.g. from an Ajax update. But that’s just wild speculation…

What we are interested in is how to fix it, and also achieve some performance gains from not continuously creating DOM elements.

The trick is to create a function which caches these canvas elements and reuses them. The easiest way is to have a create canvas function that handles all this for you. To keep a handle on the different canvases in use in Illyriad, we use a named canvas element to ensure the correct one is returned. You can call them whatever is relevant:

[code lang=”javascript” title=”Create cached canvas”]
var CachedCanvases = new Object(); // Canvas element cache

function CreateCanvasCached(name) {
if (!CachedCanvases[name]) {
var canvas = document.createElement(‘canvas’);
CachedCanvases[name] = canvas;
return canvas;
}
return CachedCanvases[name];
};
[/code]

Using this is very simple, whenever you create a canvas dynamically that is just used for composition just (i.e. you won’t attach it to the DOM) use the cache function:

[code lang=”javascript”]
var SpriteCompositionCanvas = CreateCanvasCached("sprite");
SpriteCompositionCanvas.width = 64; SpriteCompositionCanvas.height = 64;
var SpriteCompositionContext = SpriteCompositionCanvas.getContext("2d");

[/code]

I’ll do another post on some of the wonderful composition operations you can do using canvas; when this technique will become very important.

If you are updating your page using Ajax and programmatically adding and removing the display of canvas elements you will want to clear the canvas cache when you change page by adding a cache clean up to your ajax page unload function.

[code lang=”javascript”]
function CleanupCachedCanvasses() {
var delList = new Array();
for (name in CachedCanvases) {
delList.push(name);
}
for (i = 0; i < delList.length; i++) {
delete CachedCanvases[delList[i]];
}
delete delList;
}
[/code]

Now you should be good to go!

HTML5 Canvas: Animation Loop – Only work when you have to

Animating HTML5 Canvas be it 2d or WebGl can put a strain on the end user’s browser.

Trying to draw more animation frames than the browser can handle can lock it up or cause it to terminate your script. Also if the user is viewing a different tab or is in another window; burning the user’s CPU on animation they don’t even see is just squandering their resources.

Luckily, much like debouncing and throttling for user input and ajax calls, there is something we can do. HTML5 provides a neat solution for animation.  The requestAnimationFrame function allows you to put in a call-back which gets executed when the browser is ready to draw another frame. This allows the browser to throttle the animation based on the current CPU load and whether the canvas is on screen or not, resulting in a far better performance and a much lower resource usage.

However, some older browsers don’t support requestAnimationFrame, but we can use the following code to detect if they do, and if they don’t, run though some earlier implementations and finally a fall-back method to ensure we can still use the function:

[code lang=”javascript” title=”Setting up timing function”]
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (function () {
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function ( callback,element) {
window.setTimeout(callback, 1000 / 60); // Fallback timeout
};
})();
}
[/code]

To use this function we need to create an animation loop. Also if we separate our animated elements from our static elements (e.g. background) into different canvases, we only need to redraw the static elements when they change which is far less frequently.

For this we create two functions renderStatic and renderAnim; which are outside the scope of this article, but there are plenty of canvas examples to draw on. When the static elements require redrawing we just set the requiresRedraw variable to true.

Putting this altogether we end up with a animation loop looking similar to the following:

[code lang=”javascript” title=”Setting up animation callback”]
var isAnimating = false; // Is animation on or off?
var animateRunning = false; // Are we in the animation loop?
var requiresRedraw = true; // Do the static elements need to be redrawn?

function StartAnimating() { // Start animating/drawing
isAnimating = true;
if (!animateRunning) Draw(); // Only draw if we are not already drawing
}
function StopAnimating() { // Stop animating/drawing
isAnimating = false;
}

function Draw() {
if (isAnimating) { // Only draw if we are drawing
animateRunning = true;
try {
if (requiresRedraw) {
requiresRedraw = false;
renderStatic(); // function defined elsewhere
// which draws static elements
}
renderAnim(); // function defined elsewhere
// which draws animated elements
} catch (e) {
if (window.console && window.console.log)
window.console.log(e); // for debugging
}
requestAnimationFrame(Draw);
animateRunning = false;
}
}
[/code]

To start the drawing we can use StartAnimating() and to stop we can use StopAnimating()

Following this pattern should help you improve the performance of your HTML5 canvas app – while also reducing the load on your user’s computer. A double benefit!

JavaScript: Don’t spam your server, Debounce and Throttle

If you make server call-backs or update the browser GUI based on user interaction you can end up doing far more updates than necessary…

In fact on one occasion, during Illyriad-beta testing, we started tripping our server’s dynamic IP request rate throttling and banning our own IP address – D’oh!

Common situations where over responding to events are mousemove, keypress, resize and scroll; but there are a whole host of situations where this applies.

We update our pages dynamically via ajax, based on user clicks around the user interface. In this situation if the user makes several page navigation requests in quick succession – the only important one to be made is the last, as for all the previous ones the data will be thrown away. So why make the requests in the first place? It’s just unnecessary server load.

Equally, if a user is moving an mouse around a map and you are updating UI coordinates for every pixel change the mouse makes, it potentially a huge number of DOM updates far beyond the perception of a user; for no real benefit, but it will cause a degradation in performance.

Initially, we controlled the situation using complex setups of setTimeout clearTimeout and setInterval. However, as the various places we made use of this became more varied, with different sets of needs, the code became more and more complex and difficult to maintain.

Luckily, it was around this time we discovered Ben Alman’s jQuery plugins debounce and throttle; which are extremely powerful – yet simple to use of and cover a whole host of permutations.

Throttling

Using his  jQuery $.throttle, you can pass a delay and function to $.throttle to get a new function, that when called repetitively, executes the original function no more than once every delay milliseconds.

Throttling can be especially useful for rate limiting execution of handlers on events like resize and scroll. Check out the example for jQuery throttling.

Debouncing

Using his jQuery $.debounce, you can pass a delay and function to $.debounce to get a new function, that when called repetitively, executes the original function just once per “bunch” of calls, effectively coalescing multiple sequential calls into a single execution at either the beginning or end.

Debouncing can be especially useful for rate limiting execution of handlers on events that will trigger AJAX requests. Check out the example for jQuery debouncing.

In fact its well worth checking out all Ben Alman’s jQuery Projects as they are full of little wonders, that you soon are sure how you did without them!

(Fix) Memory Leaks: Ajax page replacement

Resolving memory issues with HTML replacement and Ajax

In Illyriad we do very large numbers of ajax requests using jQuery over a players session. Some of these are pure data requests, but many of them are navigational HTML page replacements.

A simple replacement seems to leak memory in various browsers:

[code lang=”js”]
$(‘#ElementToReplaceContents’).html(htmlToReplaceWith);
[/code]

Even calling the jQuery function .empty() before the replacement doesn’t seem to help in all cases. To fix this we have created a clean up function which is a common or garden “overkill” variety which seems to do the trick. Usage below:

[code lang=”js”]
function ChangeLocationSucess(data, textStatus, XHR) {
if (XHR.status == 200) {
var div = $(‘#ElementToReplaceContents’);
DoUnload(); // More on this further down
div.RemoveChildrenFromDom(); // Clean up call
div.html(data); // HTML replacement
}

[/code]

Clearing the memory from the existing HTML is done with the clean up code below:

[code lang=”js” title=”Added: RemoveChildrenFromDom jQuery plugin”]
(function( $ ){
$.fn.RemoveChildrenFromDom = function (i) {
if (!this) return;
this.find(‘input[type="submit"]’).unbind(); // Unwire submit buttons
this.children()
.empty() // jQuery empty of children
.each(function (index, domEle) {
try { domEle.innerHTML = ""; } catch (e) {} // HTML child element clear
});
this.empty(); // jQuery Empty
try { this.get().innerHTML = ""; } catch (e) {} // HTML element clear
};
})( jQuery );
[/code]

Some pages have extra resources cached and extra elements with wired up events. For these we have introduced a DoUnload function which also gets called before the replacement. This runs through the list of on page clean up functions and calls them one by one:

[code lang=”js” title=”Extra clean up system”]
var unloadFuncs = [];
function DoUnload() {
while (unloadFuncs.length > 0) {
var f = unloadFuncs.pop();
f();
f = null;
}
}
[/code]

We create these unload functions on the page that has any extra clean up to do by using the following code; which adds clean up functions to the unloadFuncs array above. This code looks like the code below:

[code lang=”js” title=”On page clean up registration”]
var unload = function() {
OnPageCachedResources.clear(); // Clear array of cached resources
OnPageCachedResources = null;
$("#OnPageElementWithWiredUpEvents")
.unbind()
.undelegate(); // Remove attached events
}
unloadFuncs.push(unload);
[/code]

Obviously this varies from page to page and most of our pages don’t need it – at which point the DoUnload function just returns with out doing work.

This coupled with the Fix for IE and JQuery 1.4 Ajax deal with most of the regular unexpected leaks.

(As an aside: When replacing HTML with Ajax, do not wire up events using onclick=”” etc in the HTML or this will also leak. Use event binding from script – a third party library like jQuery, Prototype or Dojo will make this very straight forward)

(Fix) Memory Leaks: IE and JQuery 1.4 Ajax

While testing Illyriad we found that over time the browsers IE7 and IE8 leak memory on Ajax calls using JQuery 1.4.

If you make a lot of ajax calls without changing page this can build up quite quickly and start causing issues. This is caused by the onreadystatechange event not being detached and IE not garbage-collecting the associated memory.

To fix this, locate these lines in the source JQuery file; already kindly annotated with “Stop memory leaks”:

[code lang=”js” firstline=”6019″ title=”Original jquery-1.4.4.js”]
// Stop memory leaks
if ( s.async ) {
xhr = null;
}
[/code]

Add in these extra lines to change them to the following (we detach the abort event also for good measure):

[code lang=”js” firstline=”6019″ title=”Modified jquery-1.4.4.js”]
// Stop memory leaks
if ( s.async ) {
try {
xhr.onreadystatechange = null;
xhr.abort = null;
} catch (ex) { };
xhr = null;
}
[/code]

Don’t forget to minify your jquery file before including it. [We prefer using UglifyJS]

Also rename it slightly so users with cached versions pick up the new file

e.g. jquery-1.4.4a-min.js

(Fix) Javascript: Avoiding “console is undefined”

Testing if a function or object is defined can sometimes cause as many errors as not doing so. What to do?

When developing JavaScript using either Firebug in Firefox or Chrome’s built-in console you will probably at some point be using console.log either for debugging messages or for exception information.

However, when you come to run this code in Internet Explorer, it throws an exception “console is undefined”:

[code lang=”javascript”]
try {
… // code throwing error
} catch (e) {
console.log(e); // throws script error "console is undefined"
}
[/code]

“A-ha” you think; “easily solved will just test if its defined before use”. However things are not so straight forward:

[code lang=”javascript”]
try {
… // code throwing error
} catch (e) {
if (console && console.log) // throws script error "console is undefined"
console.log(e);
}
[/code]

Even though now you are testing to see if it exists before use, you still get:

Console is undefined

While this would normally work for attributes of objects, globally scoped variables work differently. Luckily, all variables in global scope will be declared as attributes on the global window variable. This enables you to test for existence on the window.

Here we’ve wrapped the test up in a LogException function to ease changing of logging methodologies:

[code lang=”javascript”]
function LogException(e) {
if (window.console // check for window.console not console
&& window.console.log) window.console.log(e);
// Other logging here
}

try {
… // code throwing error
} catch (e) {
LogException(e); // works fine
}
[/code]

Note: Its isn’t exclusive to Internet Explorer, it just most visible there; so checking existence of globally scoped functions and variables against window rather than just by name is good practice!