How to Make a "Write-On" Effect using HTML5 Canvas & JavaScript

Published Jun 08, 2015Last updated Feb 22, 2017
How to Make a "Write-On" Effect using HTML5 Canvas & JavaScript

The HTML5 Canvas is a very powerful element that can make impressive, eye-catching effects that can be useful and interactive. HTML5 Canvas can do things such as pre-processing images before they are sent to a server.

The Purpose

This how-to guide will show you a simple technique which will emulate handwriting by animating a stroke-on effect using canvas. The text will appear to be written right in front of your eyes.

The code is simple, small, and efficient─with a great result you can use to attract attention with.

animation snapshot
(Firefox Windows rendition)

How it Works

We will take advantage of dash patterns. A dash pattern is often used to create stippled lines or marching ants like those you probably have seen in Photoshop selections.

A dash-pattern basically describes the length of an on-range and an off-range. You can add more than two if you want to create "morse" patterns for lines, etc.

We can also use this by defining very long lengths so that the on-range will seem to gradually appear. This will make our text look like the outline of it is written by hand.

We will use an animation loop that will iterate through each character in a string, loop through a dash-pattern setting variable length for the off-dash, and update to the canvas.

What We Need

First, we need a canvas element - the final size depends on the font-size, but this can be a good start for our little demo:

    <canvas width=640 height=100></canvas>

Now the code is straight forward, we define some variables:

    // get 2D context
  var ctx = document.querySelector("canvas").getContext("2d"),
  
      // dash-length for off-range
        dashLen = 220,
    
    // we'll update this, initialize
        dashOffset = dashLen,
    
    // some arbitrary speed
        speed = 5,
    
    // the text we will draw
        txt = "STROKE-ON CANVAS",
    
    // start position for x and iterator
    x = 30, i = 0;

Then let's set up some font and color style for the canvas context:

    // Comic Sans?? Let's make it useful for something ;) w/ fallbacks
    ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif"; 
  
  // thickness of the line
    ctx.lineWidth = 5; 
  
  // to avoid spikes we can join each line with a round joint
  ctx.lineJoin = "round";
  
  // increase realism letting background (f.ex. paper) show through
  ctx.globalAlpha = 2/3;
  
  // some color, lets use a black pencil
    ctx.strokeStyle = ctx.fillStyle = "#000";

The Animation Code

Ok, now that we have everything set up, let's do the main code:

    (function loop() {
      // clear canvas for each frame
      ctx.clearRect(x, 0, 60, 150);
      
      // calculate and set current line-dash for this char
      ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]);
      
      // reduce length of off-dash
      dashOffset -= speed;
      
      // draw char to canvas with current dash-length
      ctx.strokeText(txt[i], x, 90);

      // char done? no, the loop
      if (dashOffset > 0) requestAnimationFrame(loop);
      else {
      
        // ok, outline done, lets fill its interior before next
        ctx.fillText(txt[i], x, 90);
        
        // reset line-dash length
        dashOffset = dashLen;
        
        // get x position to next char by measuring what we have drawn
        // notice we offset it a little by random to increase realism
        x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
        
        // lets use an absolute transform to randomize y-position a little
        ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random());
        
        // and just cause we can, rotate it a little too to make it even
        // more realistic
        ctx.rotate(Math.random() * 0.005);
        
        // if we still have chars left, loop animation again for this char
        if (i < txt.length) requestAnimationFrame(loop);
      }
    })();  // just to self-invoke the loop

And that's it, the result will be something like this (actual result will depend on browser's text-rendering engine as well as the availability of font(s)):

animation snapshot
Firefox Windows rendition

(I made this code originally as an answer at stackoverflow.com)

Discover and read more posts from Ken Fyrstenberg
get started
Enjoy this post?

Leave a like and comment for Ken