Having fun with HTML5 – Canvas, part 2

You can become a serverless blackbelt. Enrol to my 4-week online workshop Production-Ready Serverless and gain hands-on experience building something from scratch using serverless technologies. At the end of the workshop, you should have a broader view of the challenges you will face as your serverless architecture matures and expands. You should also have a firm grasp on when serverless is a good fit for your system as well as common pitfalls you need to avoid. Sign up now and get 15% discount with the code yanprs15!

Earlier I explored some of the basic drawing methods available on the 2D context of the new canvas element in HTML5, moving on from there, I’ve put together another quick demo here (see image below) which lets the user scribble inside the canvas element.



The HTML for the page is simple enough, the key thing is obviously the canvas element:

<section id="wrapper">
    <h1>HTML5 Demo</h1>
    <aside id="message"></aside>

        <section class="block">
            <h4>Drawing tools:</h4>
                Line width:
                <input type="text" id="btnLinewidth" value="1"></input> pixels
                <input type="submit" id="btnClear" value="Clear"></input>
        <section class="block">
            <h4>Canvas area:</h4>
            <canvas id="drawingCanvas" width="400" height="400" class="block"
                    onSelectStart="this.style.cursor='crosshair'; return false;"></canvas>


The css for the canvas element is as below:

    border-style: dotted;
    border-width: 1px;
    border-color: Black;
    cursor: crosshair;

I want to use the ‘crosshair’ cursor whenever you’re in the canvas area, but the above css only works when the left mouse button is not held down (when by default, the cursor changes to the ‘text’ cursor) so in addition to the css I added an event handler to change the cursor on the select start event in the HTML mark up:

<canvas id="drawingCanvas" width="400" height="400" class="block"
        onSelectStart="this.style.cursor='crosshair'; return false;"></canvas>


Canvas support detection

When the page finishes loading I first check if the browser supports the canvas element, again using the Modernizr library, and display a warning message if canvas is not supported:

$(document).ready(function () {
    // display a warning message if canvas is not supported
    if (!Modernizr.canvas) {
        $("#message").html("<p><b>WARNING</b>: Your browser does not support HTML5's canvas feature, you won't be able to see the drawings below</p>");
    } else {

Designing the interactions

The interaction I’m after here is such that you start drawing by clicking and holding down the left mouse button and the path is drawn as you move the mouse while still holding the mouse button, and you stop drawing by releasing the mouse button or moving the mouse out of the canvas area. So ultimately it all boils down to the standard onmousedown, onmousemove, onmouseup and onmouseout events.

There’s one more problem which we have to solve first though – how to resolve the x, y coordinates of the click in relation to the canvas (rather than the page itself). The problem is that mouse events are implemented differently in different browsers, so here’s a helper function which encapsulates that particular piece of logic:

// works out the X, Y coordinates of the click INSIDE the canvas element from the X, Y
// coordinates on the page
function getPosition(mouseEvent, element) {
    var x, y;
    if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {
        x = mouseEvent.pageX;
        y = mouseEvent.pageY;
    } else {
        x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
        y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    return { X: x - element.offsetLeft, Y: y - element.offsetTop };

Mouse event handlers

First, we need to obtain references to both the canvas element and the 2D drawing context so we can use them later:

var element = document.getElementById("drawingCanvas");
var context = element.getContext("2d");

For the mouse down event, we need to first work out the x and y coordinates of the click inside the canvas then start our path at that point:

// start drawing when the mousedown event fires, and attach handlers to
// draw a line to wherever the mouse moves to
$("#drawingCanvas").mousedown(function (mouseEvent) {
    var position = getPosition(mouseEvent, element);
    context.moveTo(position.X, position.Y);

    // attach event handlers
    $(this).mousemove(function (mouseEvent) {
        drawLine(mouseEvent, element, context);
    }).mouseup(function (mouseEvent) {
        finishDrawing(mouseEvent, element, context);
    }).mouseout(function (mouseEvent) {
        finishDrawing(mouseEvent, element, context);

// draws a line to the x and y coordinates of the mouse event inside
// the specified element using the specified context
function drawLine(mouseEvent, element, context) {
    var position = getPosition(event, element);
    context.lineTo(position.X, position.Y);

// draws a line from the last coordiantes in the path to the finishing
// coordinates and unbind any event handlers which need to be preceded
// by the mouse down event
function finishDrawing(mouseEvent, element, context) {
    // draw the line to the finishing coordinates
    drawLine(mouseEvent, element, context);


    // unbind any events which could draw

The approach I have taken here is to attach the event handlers for mouse move/up/out inside the mouse down event handler (which marks the start of the drawing process), these handlers are then unbound when mouse up or mouse out event occurs (which marks the end of the drawing process).

This approach removes the need for a global flag (don’t you just hate those by now!?) to track when the code needs to draw a line. It also makes sure that the logical start and end of the ‘drawing’ event (which as stated before, starts at mouse down and finishes at either mouse up or mouse out) matches the lifetime of the handlers required to handle this so that we don’t handle the mouse move/up/out events unless we need to.

To round things off, there are two more handlers needed to clear the canvas and to change the width of the lines being drawn:

// clear the content of the canvas by resizing the element
$("#btnClear").click(function () {
    // remember the current line width
    var currentWidth = context.lineWidth;

    element.width = element.width;
    context.lineWidth = currentWidth;

// change the line width
$("#btnLinewidth").change(function (event) {
    if (!isNaN(event.target.value)) {
        context.lineWidth = event.target.value;

Oh and one more thing, you might have noticed that I’ve chained some of the method calls, such as this:


This is a simple optimization step to reduce the number of times jQuery needs to look through the DOM to identify matching elements. So that’s it, a simple HTML5 page to let you scribble in a canvas page :-)

Related posts:

Having fun with HTML5 – Canvas, part 1

Having fun with HTML5 – Canvas, part 3

Having fun with HTML5 – Canvas, part 4

Having fun with HTML5 – Canvas, part 5

Liked this article? Support me on Patreon and get direct help from me via a private Slack channel or 1-2-1 mentoring.
Subscribe to my newsletter

Hi, I’m Yan. I’m an AWS Serverless Hero and I help companies go faster for less by adopting serverless technologies successfully.

Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.

Hire me.

Skill up your serverless game with this hands-on workshop.

My 4-week Production-Ready Serverless online workshop is back!

This course takes you through building a production-ready serverless web application from testing, deployment, security, all the way through to observability. The motivation for this course is to give you hands-on experience building something with serverless technologies while giving you a broader view of the challenges you will face as the architecture matures and expands.

We will start at the basics and give you a firm introduction to Lambda and all the relevant concepts and service features (including the latest announcements in 2020). And then gradually ramping up and cover a wide array of topics such as API security, testing strategies, CI/CD, secret management, and operational best practices for monitoring and troubleshooting.

If you enrol now you can also get 15% OFF with the promo code “yanprs15”.

Enrol now and SAVE 15%.

Check out my new podcast Real-World Serverless where I talk with engineers who are building amazing things with serverless technologies and discuss the real-world use cases and challenges they face. If you’re interested in what people are actually doing with serverless and what it’s really like to be working with serverless day-to-day, then this is the podcast for you.

Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. We will also cover latest features from re:Invent 2019 such as Provisioned Concurrency and Lambda Destinations. Enrol now and start learning!

Check out my video course, Complete Guide to AWS Step Functions. In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. There is something for everyone from beginners to more advanced users looking for design patterns and best practices. Enrol now and start learning!