In a recent project, the graphic designer working in my same studio chose to apply a multiply filter on an image and to ask me to recreate the same effect on a HTML page.

Moreover, he decided that the multiply layer should fade out, showing the normal image if the user hovers the picture area.

How to do it: first way

The simplest way to obtain the live multiply effect would be to post-process each image and create two versions: the original one and another one with the multiply filter applied. This solution might be good, but:

  • firstly, the editor should have uploaded two versions of the same image;
  • secondly, the HTML page would have included two images of the same subject, so the end user would have been supposed to download both of them.

 Second way: HTML5 canvas

Another possible solution to simulate the multiply effect on the page was to do it with HTML5 canvas. As the WHATWG says:

“The canvas element provides scripts with a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, or other visual images on the fly.”

Thanks to the canvas APIs I was able to get the color values of each pixel of the image and transform them applying the multiply formula, which simply is:

Color = (Top Color) * (Bottom Color) / 255

What about the browser’s support? Well, all modern browsers support the HTML canvas.

Oddly, Internet Explorer, until version nine, does not support it. Despite Javascript libraries such as Explorer Canvas extend canvas’ support also in IE6/7/8, there is no way of managing the image data through VML.

So, with IE 6-8 and other legacy browsers we can fall back to an element positioned above with the CSS opacity value set less than one.

Canvas multiply filter

In order to obtain the filter, first of all I have added an <img> and then, with a Javascript function, I have inserted a <canvas> before the image:

imageBottom = document.getElementById('my_imageId');
canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
imageBottom.parentNode.insertBefore(canvas, imageBottom);

To manipulate the pixels, the canvas 2D Context API provides you three methods: createImageData, getImageData, and putImageData.

The first method, createImageData, is used to clone the image on the canvas:

// get 2d context and draw the image
ctx = canvas.getContext('2d');
ctx.drawImage(imageBottom, 0, 0);

The second one, getImageData, retrieves the color of each pixel of a determinate area and, after manipulating them, it lets you substitute the pixels using putImageData.

// Get the CanvasPixelArray from the given coordinates.
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
pix = imgData.data;
// Loop over each pixel and change the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = multiply(multiplyColor[0], pix[i  ]); // red
    pix[i+1] = multiply(multiplyColor[1], pix[i+1]); // green
    pix[i+2] = multiply(multiplyColor[2], pix[i+2]); // blue
    // pix[i+3] is alpha channel (ignored)
}
// Draw the result on the canvas
ctx.putImageData(imgData, 0, 0);

The multiplyColor variable is an array of RGB values; the multiply function is a method that returns the computed color value, by applying the multiply formula.

MultiplyColor = [255, 105, 0];
function multiply(topValue, bottomValue){
  return topValue * bottomValue / 255;
}

Demo and final thoughts

If you want to see the canvas multiply filter in action, have a look at this demo.

At the moment the code accepts as “top level” only a single color, so the multiply filter is between an image and a spot. But it is not so hard to replace the single color with a gradient or another image.

Furthermore, I have set up the code to apply the effect on a single element on the page. You can tweak the Javascript closure so you can be able to use the filter on multiple elements. Remember that the canvas performance is pretty good on all modern browsers, even on Android, but the canvas runs really slow on iOS devices.