The blog of dlaa.me

Using one platform to build another [HTML 5's canvas tag implemented using Silverlight!]

Background

There's been some buzz about the upcoming HTML 5 standard over the past few months. In particular, there are a couple of new features that people are looking forward to. One of them is the new <canvas> element which introduces a 2D drawing API offering a pretty rich set of functionality. If you've worked with HTML much, you can probably imagine some of the things that become possible with this. In fact, those are probably some of the same things that Flash and Silverlight are being used for today! So some people have gone as far as to suggest HTML 5 could eliminate the need for Flash and Silverlight...

I didn't know much about the <canvas> element and I wanted to understand the situation better, so I did some research. The specification for <canvas> is available from two sources: the W3C and the WHATWG. (The two groups are working together, so the spec is the same on both sites.) The API is comprised of something close to 100 interfaces, properties, and methods and offers an immediate mode programming model that's superficially similar to the Windows GDI API. At a very high level, the following concepts are defined (though Philip Taylor did some investigation a while back which suggested that no browser offered complete support):

  • Paths and shapes (move/line/curve/arc/clipping/etc.)
  • Strokes and fills (using solid colors/gradients/images/etc.)
  • Images
  • Context save/restore
  • Transformations (scale/rotate/translate/etc.)
  • Compositing (alpha/blending/etc.)
  • Text (font/alignment/measure/etc.)
  • Pixel-level manipulation
  • Shadows

There's a variety of stuff there - and as I read through the list, I was struck by how much of it is natively supported by Silverlight. Pretty much all of it, in fact! :) So I thought it might be a fun and interesting learning experience to try implementing the HTML 5 <canvas> specification in Silverlight! What better way to understand the capabilities of <canvas> than to implement them, right?

So I went off and started coding... I didn't set out to support everything and I didn't set out to write the most efficient code (in fact, some of what's there is decidedly inefficient!) - I just set out to implement enough of the specification to run some sample apps and see how hard it was.

And it turns out to have been pretty easy! Thanks to Silverlight's HTML Bridge, I had no trouble creating a Silverlight object that looks just like a <canvas> to JavaScript code running on a web page. So similar, in fact, that I'm able to run some pretty cool sample applications on my own <canvas> simply by tweaking the HTML to instantiate a Silverlight <canvas> instead of the browser's <canvas>. And as a nice side effect, Internet Explorer "magically" gains support for the <canvas> tag!

Aside: Yes, I know I'm not the first person to add <canvas> support to IE. :)

 

Samples

I started off easy by working my way through the Mozilla Developer Center's Canvas tutorial and implementing missing features until each of the samples loaded and rendered successfully. Here are three of my favorite samples as rendered by Html5Canvas, my custom <canvas> implementation:

Mozilla samples

 

Aside: It's important to note that I did NOT change the JavaScript of these (or any other) samples - I just tweaked the HTML host page to load my <canvas> implementation. My goal was API- and feature-level parity; a Silverlight implementation of <canvas> that was "plug-compatible" with the existing offerings.

 

After that, I knew I just had to run Ben Joffe's Canvascape "3D Walker" because it bears a striking resemblance to one of the games I played when I was younger:

Canvascape

 

Aside: Be sure to look for the "Silverlight" context menu item I've included in the image above and in the next few screen shots to prove there's no trickery going on. :)

 

Next up was Bill Mill's Canvas Tutorial - and another shout-out to hours of misspent youth:

Canvas Tutorial

 

For something completely different, I got Bjoern Lindberg's Blob Sallad running:

Blob Sallad

 

Then it was on to Ryan Alexander's Chrome Canopy fractal zoomer (despite the note, it runs okay-ish in IE8 with my Silverlight <canvas>):

Chrome Canopy

 

Finally, I wanted to see how the 9elements HTML5 Canvas Experiment ran:

Canvas Experiment

 

Whew, What a rush - those apps are really cool! :)

 

But I still wanted a sample I could include with the source code download, so I also wrote a little app to exercise most of the APIs supported by Html5Canvas (thanks to Mozilla for the Hypotrochoid animation inspiration and Wikipedia for the equation). Here it is on IE:

Sample application in IE

 

And wouldn't it be nice to see how Html5Canvas compares to a "real" <canvas> implementation? Sure, here's my sample running on Firefox:

Sample application in Firefox

 

Details

So how does it actually work? Well, I'm obviously not modifying the browser itself, so redefining the actual <canvas> tag isn't an option. Instead, I've written a simple Html5Canvas.js file which gets referenced at the top of the HTML page:

<script type="text/javascript" src="Html5Canvas.js"></script>

Among other things, it defines the handy function InsertCanvasObject(id, width, height, action) function which can be used to insert a Silverlight <canvas> thusly:

<script type="text/javascript">
    InsertCanvasObject("mycanvas", 200, 200, onCanvasLoad);
</script>

That code inserts a Silverlight object running Html5Canvas.xap that looks just like the <canvas> tag would have. Yep, it's that easy!

And it brings up an important difference between Html5Canvas and a real <canvas>: Html5Canvas can be used only after the Silverlight object has loaded - whereas <canvas> is usable as soon as the body/window has loaded (which happens sooner). This distinction is important to keep in mind if you're converting an existing page, because it requires moving the initialization call from onload to the action parameter of InsertCanvasObject. Believe it or not, that's really the only big "gotcha"!

Aside: While I could think of a few ways to avoid exposing this difference to the developer, none of them were general enough to apply universally.

Other minor differences between Html5Canvas and <canvas> are that Silverlight doesn't natively support the relevant repeat modes used by createPattern to tile images (though I could implement them without much difficulty), Silverlight doesn't support the GIF image format for use with drawImage (also easily worked around), and the conventional technique of JavaScript feature-detection by writing if (canvas.toDataURL) { ... } doesn't work because Silverlight's HTML Bridge doesn't allow a method to be treated like a property (I could work around this, too, but the extra level of indirection was unnecessarily confusing for a sample app).

Finally, let me reiterate that I did not attempt to implement the complete <canvas> specification. Instead, I implemented just enough to support the first 5 (of 6 total) Mozilla sample pages as well as the handful of applications shown above. Specifically, I've implemented everything that's not in italics in the feature list at the beginning of this post. Thinking about what it would take to add the stuff that's not implemented: text and pixel-level manipulation are both directly supported by Silverlight and should be pretty easy. Shadows seem like a natural fit for Silverlight's pixel shader support (though I haven't played around with it yet). All that's left is layer compositing, which does worry me just a little... I haven't thought about it much, but this seems like another job for WriteableBitmap, perhaps.

 

[Please click here to download the complete source code to Html5Canvas and the sample application shown above.]

Be sure to set the Html5Canvas.Web project as the active project and run the TestPage.html within it to see the sample application.

 

Summary

Html5Canvas was a fun project that definitely accomplished its goal of bringing me up to speed on HTML 5's <canvas> element! The implementation proved to be fairly straightforward, though there were a couple of challenging bits that ended up being good learning opportunities. :) The code I'm sharing here is intended to be a proof-of-concept and is not optimized for performance. However, if there's interest in making broader use of Html5Canvas, I have some ideas that should really improve things...

I hope folks find this all as interesting as I did - and maybe next time you want to add browser features, you'll use Silverlight, too! ;)