One of the interesting features of the HTML5 canvas API is it’s ability to manipulate pixel information at the individual byte level. Reading data and inserting it into the canvas is very simple:
1 2 3 4 5 6 7 8 9 10 11 12 | //Select our canvas and context var canvas = document.getElementById("imageCanvas"); var context = canvas.getContext('2d'); //Select the image from the DOM var selectedImage = document.getElementById("image"); //Draw the image to the canvas then read in the data context.drawImage(selectedImage, 0, 0); var originalLakeImageData = context.getImageData(0,0, width, height); //Image data is now stored in an array in the form //originalLakeImageData.data = [r1, g1, b1, a1, r2, g2, b2, a2....] |
The data is stored in one long array with 4 values for every 1 pixel in the image (red, green, blue, alpha). Once you have the data inside a variable you can start manipulating it in whatever way you like. I’ve put together 3 demos displaying some pixel manipulation in action, you can view them here.
- Slider: This demo simply shows a before and after shot of a photograph I have cleaned up in Photoshop. As you slide your mouse over the image it reads in data from each data set. I find this is quite a good way of displaying before and after shots.
- Pointer: Uses the same image data as the previous demo, only this time a region is picked out of the new image data set and displayed instead of the old image data. This gives the effect of ‘looking through’ the old image to the new one underneath.
- Black & White: This demo only uses 1 data set, depending on where you hover the mouse you get a black and white image. The image data is read for every pixel, an average of the colour is calculated and pushed back into the data set. This results in a black and white image.
Some inspiration for the experiment was taken from the extremely useful jQuery Before / after plug-in, which I’ve yet to use but do plan to in the future.
One issue you may notice is the fact that the swipe is slightly sluggish; this is because as you hover over the image, the JavaScript is looping through every pixel in the image continuously:
1 2 3 4 5 6 7 8 9 10 | //Loop through all the pixels in the image //Top to bottom for(var j = 0; j < selectedImageData.height; j++){ //Left to right for(var i = 0; i < selectedImageData.width; i++){ //Look for our individual pixel info (Times 4 due to RGBA) var index = (j * 4) * selectedImageData.width + (i * 4); //Do something with the data... } } |
That works out to be a staggering 720,000 bits of information in the data array (300px x 460px x 4) per image. The smaller your images are the smoother the effect, so there is a limit to how effective this method can be. I won’t be using it on a live project any time soon, but the demos have allowed me to experiment with manipulating image data using canvas.
Note: You may be wondering where the images are from. They are both my own images, one is of my Great Uncle Ben, who along with my Grandfather and their older brother Joe, fought in the Second World War. Here you can see him in uniform in North Africa. The second image was taken by me at Ullswater Water in the Lake District in 2006.

A on June 29 11 / 179 Permalink
Best tutorial I’ve seen ever for such a short description of what should be such a fabulous tool…if the tutorial worked it would be the net’s best ever tutorial without a doubt. But the description and example of getting the data into an array is majestic. Thanks
Matt on June 29 11 / 179 Permalink
Hi Alinullus,
Thanks for the feedback, glad you found it useful. I’ve just tested the example in Chrome and FF5 and it’s working fine. I’d imagine the example works in IE9 (hopefully), but IE8 and lower is a no-go as I haven’t added the excanvas library.
Paul Duffy on September 2 11 / 244 Permalink
Hi Matt, just to let you know that this doesn’t work in IE9 and it’s for a very old reason.
getImageData on image1 throws an INDEX_SIZE_ERR DOM exception because the received width and height are both zero. This is because (for the reader, you’re probably kicking yourself right now :op ) you’re querying the element width and height while display: none is applied to the element (or a containing element, it wouldn’t matter) and IE has always considered such elements to have zero size in all dimensions.
Last I checked, changing to display: fixed; left: -99999px works better in these situations.
Matt on September 7 11 / 249 Permalink
Hi Paul,
Ooh thanks for pointing that out. The blog post is from a while ago so hadn’t tested it in IE9. When I get time I’ll update the demo.
Thanks again,
Matt
Polo on April 16 12 / 106 Permalink
Very useful! Thanks for share.