Create text in lights with HTML5 Canvas and CSS Blend Modes

Looking at Las Vegas signs from the 1950’s put me in mind to create a sparkly lighting effect for web page text. This version is derived from my TV static article, with which it shares some code.

Making a Marquee

The markup consists of a <canvas> element, heading text, and a surrounding <div>. The h1 is contentEditable for section and alteration:

<div id="allofthelights">
    <canvas width="800" height="350"></canvas>
    <h1 contenteditable="true">Liza</h1>
</div>

The <canvas> and heading are held at the same location using position: absolute:

#allofthelights {
    position: relative;
    overflow: hidden;
}
#allofthelights canvas, #allofthelights h1 {
    width: 100%;
    position: absolute;
    top: 0;
}

As the first step to save on rendering time, the <canvas> element is given the default “off” state of the lights; where a light isn’t drawn, this color will appear instead.

#allofthelights canvas {
    background: #e97f7b;
}

The <h1> is styled with vw units to scale with the page:

#allofthelights h1 {
    font-size: 10vw;
    text-align: center;
    text-transform: uppercase;
    margin: 0;
    padding: 2rem;
}

Glitter

The script at the end of the page starts by identifying the element and setting the light cell size, together with the light color:

var nameLights = document.querySelector("#allofthelights canvas"),
context = nameLights.getContext("2d"),
lightSize = 6;
context.fillStyle = "#fbe1ca";

The central function:

function drawLights() {
    context.clearRect(0, 0, nameLights.width, nameLights.height);
    for (var v=60; v < nameLights.height - 30; v += lightSize){
        for (var h=0; h < nameLights.width; h += lightSize){
            if (Math.random()<.5) {
                context.fillRect(h + 1,v + 1, lightSize -1, lightSize - 1);
            }
        }
    }
    requestAnimationFrame(drawLights);
}
drawLights();

In order, the function:

  1. Clears the canvas space, removing any previous drawing.
  2. Starts drawing 60 pixels in from the top of the canvas (it will stop 30 pixels from the bottom) to save rendering time.
  3. Starts drawing light cells horizontally.
  4. Makes a random coin toss to see if it should draw a light cell in the current location.
  5. if the toss is “heads”, draws a square 2 pixels smaller than the light cell size to create a visible grid” pattern.

Broadway

Setting the heading text so that the canvas only shows through it uses a technique I detailed in a previous CSS blend modes article: the heading text is provided with a background fill and blend mode.

#allofthelights h1 {
    background: #fff;
    mix-blend-mode: lighten;
}

The result is what you can see at the top of this article.

Why CSS?

It’s reasonable to ask why an HTML element and CSS blend modes were used when the Canvas API has its own blend modes, clipping paths, and support for text. In this case, there were several reasons:

  1. Text written into the canvas is rendered as pixels, not readable, selectable or searchable content. If you wanted text with equal accessibility purely in canvas, you’d have to write the text equivalent between the opening and closing tags:

    <canvas width="800" height="350">Liza</canvas>

  2. There’s no concept of “centering” text in the canvas context: I’d have to read the text size in JavaScript and nudge it inside the confines of the canvas space to simulate centering.

That’s not to say that using a canvas-only approach is “wrong” – in some cases, it would make a lot of sense – just that I didn’t use it in this case.

Something a Little Less Gaudy

There are several ways you could reduce the “sparkles” of the text:

  1. You could reduce the chance of a cell being filled by lowering the value used by Math.random()
  2. You could work in an “intermediate” color between the background and the full color for each cell, providing the impression of slowing down the animation.
  3. A setTimeOut or setInterval could be used to slow the animation further.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/RGgNqz

https://codepen.io/dudleystorey/pen/RGgNqz