Randomness, Throbbers and the <canvas> tag
[caveat - if you're reading this in a feedreader, I have no idea how the iframes below will work. If they do, then great. If not, then maybe click through to the site...]
As I've been on holiday for the last week, I thought I'd take the opportunity to learn a little bit about some of the new funky stuff that's in HTML 5. The Google mobile team just released an awesome new version of gmail for iPhone and Android that relies heavily on HTML 5 and it seems that there are some pretty cool things you can do with it.
In an amusing twist, the fact that iPhone and Android browsers are based on WebKit means that for once, (smart-phone) mobile developers can build on top of the new standard with more confidence before their desktop cousins (assuming of course they don't need their sites to work on a Blackberry). On the desktop, Safari and Chrome are built on WebKit, so they are fine, and Firefox is gradually introducing all of the features through each new build to Gekko. As the standard is still evoling, no browser has comprehensive support, but as far as I can tell, WebKit is most in tune (get the nightly build for the latest bells and whistles), with Firefox and Opera not far behind and Internet Explorer (as ever) a distant fourth (IE 7 and before have none of these features, IE 8 may have some but I'm on a Mac and so haven't tested it...)
The three areas of HTML 5 that I am most interested in exploring are the <canvas> tag, the Application Cache and the Web Storage API (mainly because these are the ones that my colleagues blogged about and I caught the bug). So far I've spent a couple of days playing with <canvas> and have to say I've enjoyed it immensely. Posts on the App Cache and Storage capabilities will follow just as soon as I'm finished drawing (I promise...)
So, what is <canvas>? Well the wikipedia article has some good history, but in essence <canvas> lets you define an area of the DOM that you can subsequently use for scripted vector graphics. There is a detailed tutorial over on Mozilla Developer Center that I used to get started, which I don't intend to reproduce here. That said, here are a few useful pointers:
- <canvas> takes two explicit parameters height and width to specify the size of the area on which you can draw. Apparently setting these in CSS is not a good idea.
- You draw inside your canvas using Javascript. Get hold of your canvas object from the DOM and you'll find it is imbued with all the helpful vector drawing methods you need
- You can animate your canvas by using the Javascript setInterval() function to redraw it at regular intervals.
First order of the day was to try to recreate a fun test that I saw recently on In The Dark (a great physics blog if you're on the look out for one). The test works on the basis that humans are really good at identifying patterns (even where there are none), and that this can lead us down the wrong path sometimes. We are shown two images of seemingly randomly scattered dots and asked to identify the one that is truly random. In lab tests, humans consistently pick the wrong image. We pick the one where the dots are scattered more smoothly, because we identify 'patterns' in the clumping caused by truly random behavior that throws us off the scent. And now, by the miracle of HTML 5, here are the two images:
No prizes for which one folks normally choose as the random image. Interesting huh?
There is a really simple piece of Javascript running these simulations (and because it's Javascript you can add interactivity just like the customisation I added to the right hand box). If you want to see the code, just view the source of the frame above. The whole thing took next to no time to put together (especially given that I was learning about <canvas> at the same time). Onto my next exercise.
In their blog post the gmail team said that they had decided to build a throbber for gmail mobile using <canvas> and Javascript so that they could reduce server round trips and page weight. I thought this sounded pretty amazing (after all a throbber gif is never more than a few kB right) so I set out to see if I could recreate their efforts. After a bit of gnashing of teeth and a lot of fun tweaking my design I came up with the following 49 lines of code (1668 Bytes or just 702 when gzipped) :
function Throbber(containerId) {
this.options = {
speedMS: 100,
center: 4,
thickness: 3,
spokes:8,
color: [0,0,0],
style: "line" //set to "balls" for a different style of throbber
};
this.t = document.getElementById(containerId);
this.c = document.createElement('canvas');
this.c.width = this.t.offsetWidth;
this.c.height = this.t.offsetHeight;
this.t.appendChild(this.c);
this.throb = function() {
var ctx = this.c.getContext("2d");
ctx.translate(this.c.width/2, this.c.height/2);
var w = Math.floor(Math.min(this.c.width,this.c.height)/2);
var self = this;
var o = self.options;
var draw = function() {
ctx.clearRect(-self.c.width/2,-self.c.height/2,self.c.width,self.c.height)
ctx.restore();
ctx.shadowOffsetX = ctx.shadowOffsetY = 1;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(220, 220, 220, 0.5)";
for (var i = 0; i < o.spokes; i++) {
r = 255-Math.floor((255-o.color[0]) / o.spokes * i);
g = 255-Math.floor((255-o.color[1]) / o.spokes * i);
b = 255-Math.floor((255-o.color[2]) / o.spokes * i);
ctx.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
if(o.style == "balls") {
ctx.beginPath();
ctx.moveTo(w,0)
ctx.arc(w-Math.floor(Math.PI*2*w/o.spokes/3),0,Math.floor(Math.PI*2*w/o.spokes/3),0,Math.PI*2,true);
ctx.fill();
} else { ctx.fillRect(o.center, -Math.floor(o.thickness/2), w-o.center, o.thickness); }
ctx.rotate(Math.PI/(o.spokes/2))
if(i == 0) { ctx.save(); }
}
};
draw();
this.timer = setInterval(draw,this.options.speedMS);
};
this.stop = function() {
clearInterval(this.timer);
this.c.getContext("2d").clearRect(-this.c.width/2,-this.c.height/2,this.c.width,this.c.height)
};
};You just add this code to your site's JS, create a new Thobber object passing in the ID of the element you want your throbber to live inside and call the throb() method on it. You can use your parent element to style the background color and the options within the throbber element to customize it. For context, the gif-based throbber on a website I built a while back was fully 3,208 bytes while the code above at 702 bytes covers not just one kind of throbber but a vast array of customizations. Here is a little demo:
Obviously there are more impressive things you can do with canvas, but I really enjoyed coding this up and think I'll find it useful (especially for projects that are focussed exclusively on iPhone or Android). I hope you might too. There's even a whole framework (processing.js) designed to make scripting canvas easier. I think I may look at that next...
So it seems like canvas is a welcome addition to the HTML family (it is a lot nicer than Flash and ActionScript in my humble opinion). I guess as browser adoption increases, we'll see more and more of it.




