Skip to Content
Course content

226: Building a Simple Whiteboard/Drawing App with Canvas

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a junior dev on my team a few years back who was tasked with building a simple signature capture tool for a contract app. He spent nearly an entire afternoon frustrated because the "signature" looked like a series of disconnected, ugly dots instead of a smooth line. He was triggering a draw command on mousedown, but he hadn't quite grasped that drawing on a canvas is more like a continuous ink trail than a series of stamps. He was missing the critical link: the mousemove event.

The HTML5 <canvas> element is essentially a blank slate of pixels. By itself, it does nothing; it's just a container. To actually put pixels on that screen, we use the Rendering Context. For a whiteboard app, we're looking at the '2d' context, which gives us a suite of methods to draw paths, boxes, and circles.

Capturing the Stream of Coordinates

To make a drawing app feel natural, you have to track the state of the mouse. You can't just draw whenever the mouse moves, or the user will leave streaks across the screen every time they move their cursor to click a menu button. I always recommend using a boolean flag—something like isDrawing—to track whether the mouse button is currently held down.

The logic flows like this: when mousedown fires, you set isDrawing to true and move your "pen" to the current mouse coordinates. While mousemove is firing, you only draw if isDrawing is true. Finally, when mouseup or mouseleave occurs, you flip that flag back to false. One thing to watch out for: the mouse coordinates provided by the event are relative to the whole browser window, but the canvas has its own local coordinate system. You'll need to subtract the canvas's offsetLeft and offsetTop from the event's clientX and clientY to make sure the line appears exactly under the cursor.

Connecting the Dots with the Canvas API

Once you have the coordinates, you need to tell the canvas how to render the line. If you simply use fillRect at every coordinate, you'll get those disconnected dots my colleague experienced. Instead, we use paths.

const ctx = canvas.getContext('2d');
ctx.beginPath(); 
ctx.moveTo(lastX, lastY); 
ctx.lineTo(currentX, currentY); 
ctx.stroke();

Calling beginPath() is vital. If you don't, the canvas remembers every single line you've ever drawn in that path and re-strokes all of them every time you move the mouse, which will tank your performance within seconds. I also suggest setting lineCap = 'round' and lineJoin = 'round'. Without these, your lines will look jagged and blocky at the joints, especially when you make sharp turns.

Managing the Drawing State

A whiteboard isn't very useful if you can't change the ink color or clear the board. Since the ctx (context) object maintains a global state, you just need to update the strokeStyle property before the next stroke() call. If you want to clear the canvas, clearRect(0, 0, canvas.width, canvas.height) is your best friend.

One professional tip: always set the canvas width and height via JavaScript or HTML attributes, not just CSS. If you scale a canvas using CSS, the browser just stretches the image, which makes your lines look blurry and offsets your coordinate calculations. Keep the internal resolution and the display size in sync.




📋 Practical Task

Build a Rainbow-Stroke Sketchpad

Create a drawing application using a <canvas> element that implements the following functionality:

  • The user can draw smooth lines by clicking and dragging the mouse.
  • Implement a "Clear Board" button that wipes the canvas clean.
  • Add three color buttons (Red, Blue, Green) that change the strokeStyle of the pen.
  • Ensure that the lines have a lineWidth of at least 5px and use round line caps for a smooth, professional feel.
  • Handle the mouseleave event so that the drawing stops if the user drags their mouse off the canvas element.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.