-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
226: Building a Simple Whiteboard/Drawing App with Canvas
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
strokeStyleof the pen. - Ensure that the lines have a
lineWidthof at least 5px and useroundline caps for a smooth, professional feel. - Handle the
mouseleaveevent so that the drawing stops if the user drags their mouse off the canvas element.
There are no comments for now.