Skip to content

JavaScript – Get brightness of single character

I am making an image/video to ASCII converter. For this, I need to get the average darkness for each character I will use. I modified the answer to this question, which gets the average brightness of an image. But it keeps saying that the brightness is 0. What did I do wrong?

//this is the character I will get the darkness of
const char = "#";

const canvas = document.querySelector("canvas"),
  ctx = canvas.getContext("2d")

const charWidth = canvas.width,
  charHeight = canvas.height;

ctx.font = "30px Arial";

//for centering text
ctx.textAlign = "center";
ctx.textBaseline = "middle";

//draws text to canvas
ctx.fillText(char, charWidth / 2, charHeight / 2);

let colorSum = 0;

const imageData = ctx.getImageData(0, 0, charWidth, charHeight);
const data = imageData.data;
let r, g, b, avg;

//loops through image data
for (let x = 0, len = data.length; x < len; x += 4) {
  //r, g, and b are always 0
  r = data[x];
  g = data[x + 1];
  b = data[x + 2];
  
  avg = Math.floor((r + g + b) / 3);
  colorSum += avg;
}

const brightness = Math.floor(colorSum / (charWidth * charHeight));
console.log(brightness);
canvas {
  width: 200px;
  height: 200px;
  outline: 1px solid #000000;
}
<canvas width="30" height="30"></canvas>

Answer

For starters, please don’t set CSS properties on canvas nodes — this stretches and warps the image. Only use the HTML element attributes canvas.width and canvas.height to get and set the width and height of the canvas.

As for the main issue, all of your rgb values are 0 (black) on your canvas by default. If you print data[x+3] you’ll see the alpha channel varies from 0-255 but this is never taken into consideration.

Depending on what you’d like to do, simply average the alpha in this channel, or fill the canvas with white (a non-transparent background color) before drawing your text and then use rgb as you’re doing.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

const char = "#";
ctx.font = "30px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(char, canvas.width / 2, canvas.height / 2);

const {data} = ctx.getImageData(
  0, 0, canvas.width, canvas.height
);
let colorSum = 0;

for (let i = 0; i < data.length; i += 4) {
  colorSum += data[i+3];
}

const brightness = Math.floor(
  255 - (colorSum / (canvas.width * canvas.height))
);
console.log(brightness);
canvas {
  outline: 1px solid #000000;
}
<canvas width="30" height="30"></canvas>

You can try this with ctx.fillRect(0, 0, canvas.width / 2, canvas.height); instead of the character and you’ll get 127 as the average, which is expected because you’ve filled half the canvas with black.