HTML5 LED scroller using a Canvas Element

I remember those days when every website had a scrolling marquee. It was the pinnacle of design back in the days when it was cool to use frame-sets and table layouts! Although the marquee tag has been deprecated by W3C I thought it would be a great idea to re-create the marquee using HTML’s canvas tag. The following shows the end result:

HTML5 marquee using the canvas tag

The finished article can be viewed here. The page above is constructed using the following html:
<html lang="en">
		<meta charset="utf-8" />
		<title>LED Scroller</title>
		<script src='led_scroller.js'></script>
		<link rel="stylesheet" type="text/css" href="led_scroller.css" media="all" />
	<body onload='init();'>
			<img src="prompt.jpg" alt='Enter your phrase here'/>
			<div id='colour_container'>
				<input type="radio" name="colour" id='pink' value="Pink" checked="checked" />
				<label for="pink"><img src="pink.jpg" alt='Pink'/></label>
				<input type="radio" name="colour" id='blue' value="Blue" />
				<label for="blue"><img src="blue.jpg" alt='Blue'/></label>
		<div id='container'>
			<input type="text" id='text' value="There is only one difference between a madman and me. I am not mad." />
			<a id='imgLink' href="javascript:startAnim();"> <img src="go.jpg" alt='Go'/> </a>
		<canvas id="led" width="1200" height="343"></canvas>
			<img src="html5-canvas.jpg" alt='Drawn using HTML&apost;s canvas'/>
The source above reveals the page is split into three sections. The header which contains the “enter a phrase” statement and two radio buttons which allow the user to control the colour of the scrolling marquee. The second section contains the canvas element which will be used to draw the scrolling marquee. And finally, the last section is the footer containing the statement “HTML5’s canvas”.

By looking at the source code we can see how the scrolling marquee works. The JavaScript source code is stored in the file included using the script tag:
<script src='led_scroller.js'></script>
For those who are not familiar with HTML let me explain how we begin to draw the marquee onto the canvas. When any HTML page loads the browser will run the JavaScript code attached to the onload event of the body tag e.g.
<body onload='init();'>
The above reveals that once the browser has completed page request it will call a function call ‘init’. This function is defined within the script include ‘led_scroller.js’. It contains the following:
function init() {
	// Grab the clock element
	canvas = document.getElementById('led');

	// Canvas supported?
	if (canvas.getContext('2d')) {
		ctx = canvas.getContext('2d');

		letters_blue = new Image();
		letters_blue.src = 'letters-blue.jpg';

		letters_pink = new Image();
		letters_pink.src = 'letters-pink.jpg';
		letters_pink.onload = startAnim;

	} else {
		alert("Canvas not supported!");
The content of the function can be broken down into logical steps. The first step is to obtain a handle to the canvas element. If the canvas element is found we then proceed to ensure the browser supports the canvas. Assuming support is implemented the second step is to load the image resources. This image contains the letters we’ll use to draw the scrolling marquee. The images that will be loaded contains the following; albeit with differing colours:

When the pink image is created it is assigned an onload event because it is the default colour. When the event fires the function ‘startAnim’ will be executed. This function contains the following:
function startAnim() {

	sMessage = document.getElementById('text').value.toUpperCase();
	iDrawPhase = 0;
	iStep = 0;

	if (sMessage.length === 0) {
		sMessage = "Please enter a phrase";
		document.getElementById('text').value = sMessage;
	} else {

		// Add 5 spaces padding so the text start off right
		sMessage = "     " + sMessage;
		// Start the timer
		job = setInterval(draw, 10);
It starts by preventing any unforeseen race conditions by cancelling any running timers. This steps is essential as the user can choose to click ‘go’ button at any time, and therefore the cancel action is important as we only want one main loop running at a time! The next step then recalls the text that will be printed on the marquee. The text is then converted to upper case characters. This is because the sprite map only contains upper case – this could be expanded to include a larger set. The function then resets two global variables ‘iDrawPhase’ and ‘iStep’ – these are used within the main loop. The last step adds a five space prefix to the message. This means the first letter of the message will start on far right of the marquee and then it will scroll left to right when the animation starts. The animation is handled within the ‘draw’ function which is invoked when the ‘setInterval’ method is called.
function draw() {

	var iCounter = 0, 
		iCharCode = 0;

	for ( iCounter = 0; iCounter < sMessage.length; iCounter++) {

		iCharCode = sMessage.charCodeAt(iCounter);

		if (iCharCode > 64 && iCharCode < 91) {
			iSpriteCol = Math.abs(65 - iCharCode) % 5;
			iSpriteRow = Math.floor(Math.abs(65 - iCharCode) / 5);
		} else {
			iSpriteCol = 1;
			iSpriteRow = 5;

		drawLetter(iSpriteCol, iSpriteRow, iCounter);

	iSpriteCol = 1;
	iSpriteRow = 5;

	for (iCounter; iCounter < sMessage.length + 10; iCounter++) {

		drawLetter(iSpriteCol, iSpriteRow, iCounter);

	iDrawPhase += 1;
	iStopPoint = (27 * sMessage.length);

	if (iDrawPhase < iStopPoint) {
		iStep += 10;
	} else {

The purpose of this function is draw the characters within the phrase onto the canvas. Each letters is handled individually. The first loop iterates through each character. The code within the loop ensures the character is within the printable range. The range limits are set to match the letters available within the sprite map. Therefore, the range of printable characters contains the letters from A to Z. The source code uses the numerical ASCII code to determine whether the current character falls into this range.

A precondition to drawing a letter onto the canvas is determining the location of the letter within the sprite map. The sprite map contain a 5 column by 6 row grid. Allowing a place for each of the 26 letters within the English alphabet. The correlation between the cell and any letter can be obtained by applying simple mathematics to the ASCII code e.g.:
iSpriteCol = Math.abs(65 - iCharCode) % 5;
iSpriteRow = Math.floor(Math.abs(65 - iCharCode) / 5);
The row and column for a blank cell are used for characters that are not present within the sprite map. These two values are then passed to the ‘drawLetter’ function. This function contains the following code:
function drawLetter(iSpriteRow, iSpriteCol, iPos) {

	var xPos = (LETTER_WIDTH * iPos) - iStep;

	if ((xPos > 0 - LETTER_WIDTH) && (xPos < 1200 + LETTER_WIDTH)) {

		bPink = document.getElementById('pink').checked;
		if (bPink === true) {

			ctx.drawImage(letters_pink, iSpriteRow * LETTER_WIDTH, iSpriteCol * LETTER_HEIGHT, LETTER_WIDTH, LETTER_HEIGHT, xPos, 0, LETTER_WIDTH, LETTER_HEIGHT);

		} else {

			ctx.drawImage(letters_blue, iSpriteRow * LETTER_WIDTH, iSpriteCol * LETTER_HEIGHT, LETTER_WIDTH, LETTER_HEIGHT, xPos, 0, LETTER_WIDTH, LETTER_HEIGHT);



The screen position of the letter within the sprite map is obtained by multiplying the position of the letter within the phrase by the width of a sprite. This is then adjusted by subtracting the ‘iStep’ variable. This variable is decremented every time the timer is executed. This stepped adjustment is what achieves the animation effect. A check is then performed to ensure the calculated x position is within the bounds of the canvas (on screen). If so the letter is drawn onto the canvas at the calculated position. Additionally, as the colour is controlled by the user, the function also ensures the correct sprite map is reference.

After drawing the entire phrase the code then adds ten blank cells onto the end. This has the effect of scrolling the last character of the phrase out of view. The last section enforces the logic which calculates whether the scroll action is complete e.g. have we scrolled the last letter out of view? If so, the animation is cancelled by stopping the timer execution loop.

That concludes one’s experiment with HTML5′s canvas. I hope this helps someone out there. All the code above may be taken and used for anything – credit not required. A working version of this project can be viewed here. The source code for this project can be obtained from my Git Hub repository here. Full credit for the original digital image goes to this site for providing the PSD.

After this post was published I got some great feedback about the scroller. Quote Christian Engel, "The demo looks really weird – you took the name "LED scroller" literally and are scrolling the LEDs! You know, in the real world, the LEDs would remain in place and only switch on/off their lights to achieve the scrolling effect.". Thus, I created a version 2 to do exactly what Christian suggested. The result of which can be viewed here.

13 thoughts on “HTML5 LED scroller using a Canvas Element

    1. To repeat you would have to reset the scroller’s variables instead of calling clearInterval(). For the time interval: I assume you mean display the current date and time? If so, there are built in JavaScript function to obtained these. Simply call them and set the text of the scroller.

      1. This is way beyond awesome. How hard would it be to add numbers? I wish I had 1/10th of your talent.

      2. Thanks for the high praise. In my case it’s not talent rather that persistence mixed with a thirst for learning. Adding numbers should be quite simple. It would simply involved adding the number to the sprite map and tweaking the table row/column lookup code.

  1. Hi there, i need to make something like this but with some tweak, different surfaces, do you think its possible ?

  2. I don’t see how to download this. The link doesn’t work. It gets an XSS CPS error. And if I download that page, it can’t find most of the files (images, CSS, JS).

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s