How to make Dynamic Text Overlays on Images
Tags: javascript, showdev, beginners, webdev
Published: 2019-02-07 11:49:11

---
title: How to make Dynamic Text Overlays on Images
published: true
description: A cool experiment in canvas you should not miss out 😬
tags: javascript, showdev, beginners, webdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/o0z8aphkdm0kj1d99onm.png
---
If you've ever made a webapp, you must be familiar with this scenario. So many times, you find yourself in a situation where you have to put some text on an image. And if the image is uploaded by a user, then it is hard to predict what color of text would go with the image. Consider the following example :

![](https://i.imgur.com/hUikRsI.png)

The text *Rachel Rose* looks perfect on a dark background, but what if the background was white? That may cause a problem because you can never predict what a user will upload.

An easy solution is to use a blur filter on the image like this...

```css
    .image-blur{
        filter: blur(1px);
    }
```
...or make a dark overlay on the image. There many ways you can do, and they all rely on making changes to the image. But what if instead of changing the image, we could change the text color?

"Well, why not?" is what I told myself when this thought crossed my mind recently. So let's explore how to do that in this post. 

![](https://media.giphy.com/media/xT0xemNCRU4TRrGDAI/giphy.gif)

### How to do it?

I have created a simple page to demonstrate this problem. If you wanna learn as you go, just clone this repo. 

{% github sarthology/dynalay-demo %}

Once cloned, use this commit to check out the unsolved problem.

```bash
   git checkout 0817434   
   npm install
   npm run start:dev
```

So currently, we have a webpage that has two types of backgrounds: dark and light. It also has two buttons to switch between them. This is how it looks like in the beginning.

![](https://thepracticaldev.s3.amazonaws.com/i/hwp2buzhp7c1wb8nkjog.gif)

To achieve this, we will use the canvas. The idea is to load the image in the canvas and then we will fetch each and every pixel of the image. The color of each pixel will be then converted into its *RGB* value. So if you average the three values, you will get the lightness of that pixel (read more [here](https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness)). The values will be between 0 (darkest) and 255 (brightest). So by comparing this for each pixel, we will get if the image is dark or not.

Great, now as we understand the algorithm, let's write some code.
![](https://media.giphy.com/media/CFoV7AtVdp1sc/giphy.gif)

First, make a function that will load src from the background into a hidden `img` element.
```javascript
    const isDark = (src)=>{
        //create a hidden img element
        let img = document.createElement("img");
        img.src = src;
        img.style.display = "none";
        document.body.appendChild(img);
    }
```

Then draw the image on canvas using

```javascript
const isDark = (src)=>{

    //create a hidden img element
    let img = document.createElement("img");
    img.src = src;
    img.style.display = "none";
    document.body.appendChild(img);

    img.onload = function() {
        // create canvas
        let canvas = document.createElement("canvas");
        canvas.width = this.width;
        canvas.height = this.height;

        let ctx = canvas.getContext("2d");
        ctx.drawImage(this,0,0);
    }
}
```
Now we need to get the RGB value of each pixel. For that, let's use `getImageData`  

```javascript
    let imageData = ctx.getImageData(0,0,canvas.width,canvas.height);
```
The output will be something like 

![](https://thepracticaldev.s3.amazonaws.com/i/5s83lt1pq4afl9gf93cd.png)

Great! Now we have the data, but it's a huge array in which each pixel's RGB value is separate like this
![](https://thepracticaldev.s3.amazonaws.com/i/lgilubqqzfulus4vfz1l.png)

So we have to loop through four of them together like 

```javascript
        let imageData = ctx.getImageData(0,0,canvas.width,canvas.height);
        let data = imageData.data;
        let r,g,b,avg;
        for(let x = 0, len = data.length; x < len; x+=4) {
            r = data[x];
            g = data[x+1];
            b = data[x+2];
            avg = Math.floor((r+g+b)/3);
        }
```
`avg` now has the average RGB value of each pixel. Next, add the average of all the pixels and divide that by total pixels in the image to get the average brightness.

```javascript
       let brightness = Math.floor(colorSum / (this.width*this.height));
```

So the final function will look something like this: 

```javascript
const isDark = (src) => {
    return new Promise((resolve, reject) => {
        //create a hidden img element
        let img = document.createElement("img");
        img.src = src;
        img.style.display = "none";
        document.body.appendChild(img);

        let colorSum = 0;
        img.onload = function () {
            // create canvas
            let canvas = document.createElement("canvas");
            canvas.width = this.width;
            canvas.height = this.height;

            let ctx = canvas.getContext("2d");
            ctx.drawImage(this, 0, 0);

            let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            let data = imageData.data;
            let r, g, b, avg;
            for (let x = 0, len = data.length; x < len; x += 4) {
                r = data[x];
                g = data[x + 1];
                b = data[x + 2];

                avg = Math.floor((r + g + b) / 3);
                colorSum += avg;
            }

            let brightness = Math.floor(colorSum / (this.width * this.height));
            resolve(brightness >= 128 ? false : true);
        }
    });
}
```
Let's use this function to check if the image is dark or not. 

```javascript
    isDark('./images/dbg.jpg').then((data) => {
        dynamicHeader.classList.remove("light-text");
        if(data) dynamicHeader.classList.add("light-text");
    });
```
So here is our result:
![](https://thepracticaldev.s3.amazonaws.com/i/uqzltnyx2cxdavq6a299.gif)

But something is missing. Aha, some style!

```css
.dynamic-header {
    transition: color 2s;
}
```
![](https://thepracticaldev.s3.amazonaws.com/i/htoqfs5csmyp8o6gw8ug.gif)

Now it's perfect. 

#### Conclusion
Hope you guys liked this small experiment and learned something useful from it. There are many ways to achieve the same results, so feel free to comment below if you have one. You may or may not want to use this in your real projects, but I am thinking of making an npm package, to take this to the next level. If you wanna join, DM me [@sarthology](https://twitter.com/sarthology) or just comment below. 

Okay guys! Will see you next time. Thanks for reading this.

![](https://media.giphy.com/media/DjclSiVtiB11C/giphy.gif)