Skip navigation

Let's talk about a very experimental approach for loading images. Every external file request is expensive over a 3G connection. Sure, things are getting better and speeds are improving, but 3G will be the IE6 of the mobile era.
During the writing of TweetStream, a mobile app which aggregates twitter - filtering out tweets for certain hashtags, I found that each tweet’s user icon was costly when loading the app. You can’t view a tweet without an image next to it, so it wouldn’t feel right without loading the image. The problem was with one image out of the 100’s that were being loaded. It took this one image over a second to load.
Seeing the problem and feeling helpless about the bottleneck, I came up with the following prototype. The only problem is that CORS (Cross-Origin Resource Sharing) served images are new and not served up by many public image services...yet. Luckily, Google is doing it with Picasa images and I’m not sure who else is, so please comment if you know of any others.
The concept is simple - load the image via Javascript, draw it into a canvas tag, base64 the image data, store it into localStorage, and return the base64 url back to the DOM when offline or on a slow connection. In conjunction with the applicationCache you would have the ability to create a ass kicking off line mobile application. localStorage is limited to 5MB, but it still gives you an option that isn’t available out of the box today.

 

In this code, we provide an image URL and base64 it via the canvas tag. Note the use of “crossOrigin” attribute. This is a feature recently provided in the Webkit nightly build.

function cacheExternalImage(url) {
    var img = new Image(); // width, height values are optional params
    //http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
    //remote server has to support CORS
    img.crossOrigin = '';
    img.src = url;
    img.onload = function() {
        if(img.complete){
            //this is where you could proxy server side
            load(img);
        }
    }
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;


    // Copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0,0);
    img.src = ctx.canvas.toDataURL("image/png");
    return img
}


Again, this is very experimental and still needs some polish. If you had to use external images that are not being served up with CORS headers, you can proxy the image server side and use the same approach making the image seem local. It would probably be even more efficient to do the base64 server side in this case.
This is just a proposal and not a production solution. Let us know if you have had a better experience.