Freehand drawing with the Canvas API

Freehand drawing with the Canvas API

●1 ●1 ●6
calendar_today ago • schedule2 min read
— Originally published at dev.to

When I was trying Canvas, I was completely puzzled. It felt so frustrating; I felt like I was writing in a completely different language instead of my usual JavaScript. But after reading the MDN doc and watching some videos...it still feels difficult and frustrating. But despite everything, let's try to build something. Polishing can come later.

First, we need to select the ID from HTML

const canvas = document.querySelector("#draw");

We need canvas.getContext here to tell the browser what type of drawing we are going to do. For now, the screen is blank, so we need to tell it what we are going to do. The <canvas> element has a method called getContext. That's the one we are targeting here

const ctx = canvas.getContext("2d");

We are going to draw on the whole window, so we need to target the whole window. But we can't just let HTML decide the window. We need to overwrite it, and JavaScript can help here with its innerHeight and innerWidth properties

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

That's a lot. I know. So, let's slow down and draw our first rectangle.

function draw() {
  const canvas = document.querySelector("#draw");
  const ctx = canvas.getContext("2d");

  ctx.fillStyle = "rgb(200 0 0 / 50%)"; // setting the rect color with the fillStyle property
  ctx.fillRect(10, 10, 50, 50); // drawing the rectangle using the fillRect canvas api's method
}
draw();

That was easy, but we are not going to draw shapes. Our goal is to use freehand. Comment out or delete the previous draw function.

For now, we need four things before going forward. Set the color of the pen, how we want our pen to be, how the ends of lines look, and the size of the pen.

ctx.strokeStyle = "rgb(200 135 90)";
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.lineWidth = 5;

Here we can set the pen color with strokeStyle, how corners between connected lines look with lineJoin, the ending of the lines with lineCap (default to butt), and the size with lineWidth.

We don't want to draw with every movement our mouse do. Only when we click the mouse and move it. So here it is

let isDrawing = false;
let lastX = 0;
let lastY = 0;

lastX/Y start at 0 as a placeholder. Later, they get replaced by the real mouse X/Y position from e.offsetX/Y.

Let's draw it now:

function draw(e) {
  if (!isDrawing) return;
  console.log(e);
  ctx.beginPath();
  ctx.moveTo(lastX, lastY);
  ctx.lineTo(e.offsetX, e.offsetY);
  ctx.stroke();
  [lastX, lastY] = [e.offsetX, e.offsetY];
}

The condition prevents drawing when the mouse button is not being held down
beginPath() starts a new path every time
moveTo() sets the starting point of the line
lineTo() draws a line from the current point to the current mouse position
stroke() draws the path onto the canvas using the current stroke settings
[lastX, lastY] = [e.offsetX, e.offsetY] updates to the current mouse position by destructuring

// Mouse button pressed down → start drawing
canvas.addEventListener("mousedown", (e) => {
  isDrawing = true;
  [lastX, lastY] = [e.offsetX, e.offsetY];
});

// Mouse button released → stop drawing
canvas.addEventListener("mouseup", () => {
  isDrawing = false;
});

// Mouse leaves canvas → also stop
canvas.addEventListener("mouseout", () => {
  isDrawing = false;
});

// Mouse moves → only draw if the flag is true
canvas.addEventListener("mousemove", draw);

That's it for today. Tada! Leave a comment if you want to.

Further reading:

  1. Drawing shapes with canvas
  2. Canvas tutorial
  3. HTML Canvas

2 Comments

0 votes
1
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Tuesday Coding Tip 02 - Template with type-specific API

Jakub Neruda - Mar 10

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

Trying Declarative Partial Updates: A Future API for Replacing HTML Later

nyaomaru - Jul 5

Solved: blocked a frame with origin "null" from accessing a cross-origin frame

James Dayalverified - Mar 14, 2025
chevron_left
160 Points • 8 Badges
Bangladesh • armaneousuf.vercel.app
2Posts
1Comments
1Connections
The key to success is to distract the distractions

Related Jobs

View all jobs →

Commenters (This Week)

3 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!