DEV Community

loading...
Cover image for Vanilla JavaScript canvas images to black and white

Vanilla JavaScript canvas images to black and white

dailydevtips1 profile image Chris Bongers Originally published at daily-dev-tips.com ・2 min read

Yesterday, we saw how to use images on our canvas and even invert the colours.

But what if we want to convert them to only three colour options?

The colour options we will be using are;

  • black
  • white
  • grey (only 1 type!)

This will abstract our image and teaches us how to create grayscale images manually.

Today's end result will look like this:

Monochrome image

JavaScript

As you could see in yesterday's article as well, we are using the getImageData function.

const img = document.getElementById("eeveelutions");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

img.onload = function () {
  ctx.drawImage(img, 0, 0);
  const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  // Code comes here
};
Enter fullscreen mode Exit fullscreen mode

This returns rgba values so as yesterday we need to loop over every 4th child.

for (i = 0; i < imgData.data.length; i += 4) {

}
Enter fullscreen mode Exit fullscreen mode

Ok, so now what we get 4-pixel values, being rgba.
The alpha we won't use, but we want to get one combined value for the rgb.

Let's add up the three values for red green and blue.

let count = imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2];
Enter fullscreen mode Exit fullscreen mode

This will give us a pixel number between 0 (black) and 765 (white).

In our case, we also add one grayscale layer, so we get the following three calculations:

  • 0-255 = black
  • 256-510 = gray
  • 511-765 = white

That being said we can have the following code:

let colour = 0;
if (count > 510) colour = 255;
else if (count > 255) colour = 127.5;
Enter fullscreen mode Exit fullscreen mode

Here we defined our default colour to be black (0), our white (255) and our gray (127.5)

We can then append our colour to the first three values of the pixel, and 255 to our alpha layer.

imgData.data[i] = colour;
imgData.data[i + 1] = colour;
imgData.data[i + 2] = colour;
imgData.data[i + 3] = 255;
Enter fullscreen mode Exit fullscreen mode

Then we need to put the data back to our canvas.

ctx.putImageData(imgData, 0, 0);
Enter fullscreen mode Exit fullscreen mode

There we go, we just converted our image into three colours!

Have a play around on this Codepen.

Moving to full black & white

We can even make it pure black and white by using the following calculations:

  • black = 0 - 382
  • white = 383 - 765

And it will result in the following loop:

for (i = 0; i < imgData.data.length; i += 4) {
    let count = imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2];
    let colour = 0;
    if (count > 383) colour = 255;

    imgData.data[i] = colour;
    imgData.data[i + 1] = colour;
    imgData.data[i + 2] = colour;
    imgData.data[i + 3] = 255;
}
Enter fullscreen mode Exit fullscreen mode

Find this example on the following Codepen.

Browser Support

The imageData API, as well as canvas, have very good support!

HTML Canvas imageData support

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Discussion (4)

pic
Editor guide
Collapse
thinhvu profile image
Thinh Vu • Edited

Sum 3 color channels then divide for 3 it's not a correct way to get the output color.
You may need to convert your image into grayscale before convert it to bitonal by setting the threshold.
en.wikipedia.org/wiki/Grayscale

Collapse
dailydevtips1 profile image
Chris Bongers Author

I did try that, but for some reason, the result was actually very bad then, this seemed to have a valid result.
I am aware it's not a proper way to determine the grayscale representation.
Let me try and set up a demo doing the grayscale first to showcase how that would look.

Collapse
cilly_boloe profile image
Billy Coloe

It's quite interesting to see the logic behind these transformations. And also really makes me appreciate CSS filters 😅

Collapse
dailydevtips1 profile image
Chris Bongers Author

Haha yeah CSS Filters, make this all vanish, but still cool to have a play with Canvas.