While testing different methods of loading images for Flax’s tilesystem, I toyed a little bit with images encoded in Base64, rather than loading them from an external location. We ended up not using it, and I’ll explain why. Now with added (preliminary) benchmarks!

How to load images from base 64 with canvas

Here’s how I did the benchmark. (If you’re not interested in the how, and just want to know what’s fastest, skip to the bottom. They’re bold, you can’t miss them.)

First, I wrote a little HTML to set up a canvas on a blank page, surround it with a small black border, and threw in a h2 tag to hold my results.

<!doctype html> <html> <head> <style> #canvas {border: 1px black solid;} </style> <script src="loadimages.js"></script> <title>Base 64 Image Loading</title> </head> <body onload="draw();"> <canvas id="canvas" width="2560" height="2048"></canvas> <h2 id="times"></h2> </body> </html>

Nothing special, really. Next, I did the oh-so-fun javascript. That looks like this:

function draw() { var average = 0; var end = 0; var start = 0; start = (new Date).getTime();//Start time end = draw64(); average += (end-start); document.getElementById("times").innerHTML += "The image took " + (end-start) +"ms to load. "; } function draw64() { var img_src = 'data:image/png;base64'; //a big-ass base64 string here //var img_src = 'testImage.png'; //the same image as above, except a png, here commented as I'm testing the base64 var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.onload = function(){ ctx.drawImage(img,0,0); } img.src = img_src; return (new Date).getTime();//end time }

It’s not the nicest javascript I’ve ever written, but it did the job for a fairly quick and dirty test. As you can hopefully see, the test benchmarks the time it takes to load the image, both if it’s a .png or if it’s base64.

Now, eagle-eyed viewers may have noticed something: there’s no base64-encoded image in that code segment. That’s because it’s effing huge. It would easily make this page ten times longer. Also, you can’t put in any line breaks, so it’d screw with all kinds of formatting and make you horizontally scroll. For example, for a 373KB PNG-encoded image (of The Legend of Zelda: Link’s Awakening‘s map, no less), there were approximately 494,738 characters. That’s probably longer than all the content on this blog put together.

This means the image, when base64-encoded, is over 100KB larger. (I have absolutely no idea why this is the case. If anyone would care to enlighten me, I’d be grateful.)

The real question is whether this overcomes the latency from asking the server to give me another file. And the real real question is whether or not the cumulative latency takes longer than the loading of the slightly larger base64 images.





Well, let’s find out, shall we?

The Benchmark

All tests were done with Google Chrome 7.0.517.24 beta, on Mac OS X 10.6.4. The cache was cleaned between each test. These tests are preliminary tests only. My internet connection is sub-optimal, your mileage may vary. Take everything I say with a pinch of salt.





Local tests

First, I simply locally tested how quickly the images themselves loaded. Specifically, I tested how quickly a base64 image would throw the onLoad event, and then I did the same for the identical, png-encoded image. I moved the script inline to the html so it’d only ask once. This test was done locally.













Strangely, Chrome rendered the base64 image in 36ms, and rendered the png in 184 seconds. However, after a reload, the image loaded in “0ms”. Even after clearing the cache, Chrome was adamant that it could load images faster than light. I imagine the latency the first time was a warm up, and so, I’m going to strike it from the records.

Average load time for local png: 2ms.

Average load time for local base64: 17ms.

The tests were run twenty times each, alternating, and clearing the cache between each.

Hosted tests

Now, obviously, local tests mean very little. There’s no server lag, for a start. For this test, I set up a quick and dirty hosting solution with Dropbox, though I intend to “properly” host them sooner or later.

I essentially did the same test, except hosted. Having cleared the cache for what must have been the fiftieth time today, I set about getting some cold, hard, grapefruit-flavoured facts.

Average load time for hosted png: 2ms.

Average load time for hosted base64: 23ms.

Well damn, there goes that whole server latency thing. Preliminary tests would show that, yes, the hivemind is right, and for good reason. Leave your images separate. For now.





Next time (hopefully this time next week, lest Ciarán throw me down a flight of stairs), I’ll do an even better benchmark. Something to do with fifty images all being loaded, as that’s much closer to a “real-world” implementation. I’m not doing it right now because it’s three in the morning, I have class in six hours, and this post is what, like a month late.





If you have any comments, especially on what I’ve done wrong, and what I can do better, I’d be extremely grateful.





Carl out!