Interaction
You can draw, style, transform, and animate. But so far the user is just watching. This lesson makes the canvas respond to mouse input — tracking position, detecting clicks on shapes, adding hover effects, and dragging objects around. Once you can do this, you've basically got a game engine.
Mouse position
The canvas doesn't know about mouse coordinates automatically. You listen for mousemove on the canvas element, but the event gives you page coordinates (clientX, clientY). To get canvas coordinates you subtract the canvas's position on the page using getBoundingClientRect().
Move your mouse over the canvas after hitting Run. Notice the onCleanup call — it registers a function that removes the event listener when you hit Run again or Stop. Without it, clicking Run multiple times would stack up duplicate listeners.
Click detection
Canvas doesn't have built-in “click this shape” events — it's just pixels. You need hit testing: when a click happens, check whether the click coordinates are inside any of your shapes. For circles, that's a distance check: Math.hypot(dx, dy) < radius.
Math.hypot(dx, dy) gives the distance between two points — if it's less than the radius, the click is inside. For rectangles you'd check mx > x && mx < x + w && ... instead. Try adding more circles or changing the shapes to rectangles with your own hit test.
Hover effects
Hover is just mousemove + hit testing. Each frame, check which shape the mouse is over and draw it differently. You can also change canvas.style.cursor to give the user a visual hint that something is interactive.
Notice the shadowBlur glow effect on hover, and the cursor changing to a pointer. The hit test here is a simple rectangle bounds check. Try adding a click handler that does something when a button is clicked.
Dragging
Dragging needs three events: mousedown starts the drag, mousemove updates position, and mouseup ends it. A key detail: calculate the offset between the mouse and the shape's centre on mousedown, then apply that offset during mousemove. Otherwise the shape snaps its centre to the cursor, which feels wrong.
The offset trick is what makes it feel smooth — the ball moves with your mouse, not jumping to snap its centre under the cursor. Try removing the offset (set offsetX/Y to 0 in handleDown) and feel the difference.
Multiple draggable objects
Same pattern, but with an array of objects. On mousedown, loop through in reverse order (so topmost draws last, gets picked first) to find which one was clicked. Move it to the end of the array so it draws on top — instant z-ordering.
Drag any circle — it pops to the front. The reverse loop means the visually topmost circle always gets picked first. Try adding more circles to the array, or making them different sizes. Can you add a constraint so they can't be dragged outside the canvas?
Putting it all together
Let's build a mini drawing app. Click and drag to draw freehand strokes. Pick a colour from the palette at the top. Hit the clear button to wipe the canvas. Every technique here — mouse tracking, hit testing, state management, continuous rendering — is something you've already learned.
A fully interactive drawing app in ~100 lines. Try adding a line width selector, an eraser (draw in the background colour), or an undo button that pops the last stroke off the array. Can you add a “save” button using canvas.toDataURL()?