How to make Dynamic Text Overlays on Images

Sarthak Sharma on February 07, 2019

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... [Read Full]
markdown guide

There are hacky CSS ways to do this, though I'd never suggest using them.


I'm really not familiar with CSS, what exactly is hacky about it?


It's these 3 lines that I don't like.

  background-clip: text;
  color: transparent;
  filter: invert(1) grayscale(1) contrast(9);

What we're doing is

  • removing bits of the span not covered by text
  • making the text invisible
  • applying filters to the span
    • inverting the colour
    • setting it to grayscale
    • setting the contrast high so we just get black and white.

But it doesn't know where the text is over the image, so it gets the colours from the top left corner.

There is a slightly less hacky option in the works but, currently, it doesn't work with background-clip. It's called background-filter


@link2twenty As I read your "why's it hacky" explanation, my thoughts were:

Nah, that's not hacky, that's brilliant.
Still not hacky.
Still loving it.
Grayscale? Such a cool idea.
And contrast to finish. Perfect. Why is any of this hacky?

"But it doesn't know where the text is over the image, so it gets the colours from the top left corner." -- Ahhhhh.... and that's where the hack hits the fan.

So close!


Far from perfect. The isDark function is extremely naive. For example - if the image has a patch of light or dark that lines up with the text, and the rest of the image is of the opposite 'lightness', you are going to end up with unreadable text again.

What you need to do is test the part of the image that is actually underneath the text


That's also doable, using the same concept and some more work in canvas.


Would it be possible to identify the subset of pixels that represent the area under the text from the image data? Then we could reapply the algorithm to a smaller size making it significantly less complex. My main concern with the original implementation would be the cost of scanning a high-resolution image. The profiling algorithm could probably be best applied after the upload on the upload service itself and store the profiling data with the image then we can just fetch the image and it's profiling data and don't have to scan it on client's device.

I think it can be. But it might take a lot of werid calculations to do something like that. But Iโ€™m up for the challenge. ๐Ÿ˜Ž

It's not to bad as long as you know (or can get) three things:

  • the size the image will be when rendered
  • the pixel position of the text relative to the top-left corner of the image
  • the approximate height and width of the text when rendered

That last bit is the hard part, especially if there's any line breaks involved. But after you have those, it's basically this:

const start = { x: img left + text rel left, y: img top + text rel top }
const end = { x: img left + text rel left + text width, y: img top + text rel top + text height }
for (const x = start.x; x < end.x; x++) {
  for (const y = start.y; y < end.y; y++) {
    const p = (canvas.width * y + x) * 4
    const [r, g, b] =[], p, p+3)
    // fancy logic of choice

Totally awesome! I faced this issue quite alot, always fixed it using a darker overlay on the image ( the easy way ). Would totally like to join on the npm package. But i am wondering what would happen if its a high res image. Would it take a while to go through all the pixels ?


Well, in current example both images used are of huge resolutions.So they take approximately same amount of time as browser take to render it.


For other inspiration, you might want to check the 7 Rules for Creating Gorgeous UI by Erik D. Kennedy. The article first released back in 2014, but I think it's still relevant until today.


Great post, thanks for sharing it man. ๐Ÿ‘


I'd suggest a shadow/outline for most practical uses.
For one thing, you are calculating the average of the whole image.
But imagine if most of the image is white, while exactly where the text goes is a black rectangle?
Then you have to calculate that exact position.
But what if that area is striped?

@link2twenty 's "hacky" CSS solutions will probably be more performant AND more effective, being able to invert the image color with the text.


This is amazing!!!! How fun. I really enjoyed the entire turorial and learned a lot. Very well explained and made perfect sense. Thank you!


I'd never really thought about the fact that averaging the R G & B values of a color would get the lightness. It makes sense, it's just never something I'd thought about. Great write up.

code of conduct - report abuse