Duotone in JavaScript
Spotify's new brand identity is full of bright colours. Here is the same effect but in js.
Demo & Code: http://codepen.io/72lions/pen/jPzLJX
One of the ways to achieve this result is the well known duotone process where you pick two colours (one for the highlights and another for the shadows) and you replace all colours in the picture with these two.
Sounds simple, right? Well it actually is. This is what you need to do:
Convert the image to grayscale
This is very simple. The way you do it is by calculating the weighted average of the R, G, B channels. Then you apply that value into every channel. For example: let's imagine that at a specific pixel has a colour with the following RGB values: 100, 200, 84. The average would be (100 + 200 +84) / 3 = 128. Then you replace the values of each channel with the new number, 128 in our case. You do that for all the pixels in your image.
for (var i = 0, offset, r, g, b, a, avg; i < pixelCount; i++) { offset = i * 4; // Gets every color and the alpha channel (r, g, b, a) r = pixels[offset + 0]; g = pixels[offset + 1]; b = pixels[offset + 2]; a = pixels[offset + 3]; // Gets the avg avg = Math.floor(0.299 * r + 0.587 * g + 0.114 * b); r = avg; g = avg; b = avg; }
Create a gradient between the colours
So, by now you should have a grayscale image. Next step would be to create a gradient between the two colours. That process is also quite simple. You decided how many steps the gradient will have. I believe that 255 steps are enough.
// Creates a gradient of 255 colors between color1 and color2 for (var d = 0; d < 255; d += 1) { var ratio = d / 255; var l = ratio; var rA = Math.floor(color1[0] * l + color2[0] * (1 - l)); var gA = Math.floor(color1[1] * l + color2[1] * (1 - l)); var bA = Math.floor(color1[2] * l + color2[2] * (1 - l)); gradientArray.push([rA, gA, bA]); }
Swap the colours
For applying the new colours we first need to know the Luminance of each pixel. We can find it quite easily by using an rgbToHSL(r, g, b) algorithm. The last value is the luminance value. Now that we have the luminance value for any given pixel, we can use that value (0 - 254) to pick a colour from the initial gradient and replace the already existing value.
We then put the data into the canvas and call dataURL() which will return a base64 encoded image.
Word of advice
If you want to use this in production, you should know that this is a heavy operation. It might block the main thread for quite some time and you might miss 60fps. That's why I recommend that you put as much code as you can into a Web Worker.
That's it. If you have any questions don't hesitate to contact me.