Canvas Shapes

Canvas paths are the building blocks behind every shape you draw — this lesson shows how beginPath() and closePath() control where a shape starts, stops, and connects.

What Is a Path in Canvas?

A path in the Canvas API is an internal list of points and the straight or curved segments that connect them. You build a path with drawing commands such as moveTo() and lineTo(), then make it visible by calling stroke() to draw its outline or fill() to paint its interior. Until one of those rendering calls runs, nothing appears on screen — the path exists only as data inside the 2D rendering context.

Starting Fresh with beginPath()

Calling beginPath() clears the list of sub-paths the context is currently tracking and resets the current point. You should call it at the start of every shape that is not meant to connect to the previous one. If you skip beginPath(), the next moveTo() or lineTo() call continues appending to whatever path segments already exist, which can silently reintroduce old geometry into your next stroke() or fill().

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="300" height="200" style="border:1px solid #ccc;"></canvas>

<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.moveTo(50, 140);   // starting point, nothing drawn yet
ctx.lineTo(150, 140);  // bottom edge of the triangle
ctx.lineTo(100, 40);   // up to the peak
ctx.closePath();       // straight line back to (50, 140)

ctx.strokeStyle = '#2563eb';
ctx.lineWidth = 3;
ctx.stroke();
</script>

</body>
</html>
Note: Get in the habit of pairing every shape with its own beginPath(). It costs nothing, and it prevents the classic bug where an old shape's last point becomes the invisible start of a brand-new one.

Closing the Loop with closePath()

closePath() draws a straight line from the current point back to the first point of the active sub-path and marks that sub-path as closed. This matters most when you stroke a shape: without it, the final edge is simply never drawn, leaving a visible gap, and the corner where the shape should meet stays two separate open ends instead of a joined corner. fill() is more forgiving — it always treats an open sub-path as implicitly closed for the purpose of coloring its interior — which is why a shape can look filled correctly but stroke with a missing edge if you forget closePath().

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="300" height="200" style="border:1px solid #ccc;"></canvas>

<script>
const ctx = document.getElementById('scene').getContext('2d');

// First shape: a small square
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(80, 20);
ctx.lineTo(80, 80);
ctx.lineTo(20, 80);
ctx.closePath();
ctx.fillStyle = '#16a34a';
ctx.fill();

// Second shape: an unrelated triangle
ctx.beginPath();
ctx.moveTo(150, 80);
ctx.lineTo(220, 80);
ctx.lineTo(185, 20);
ctx.closePath();
ctx.fillStyle = '#f97316';
ctx.fill();
</script>

</body>
</html>
  • moveTo(x, y) repositions the current point without drawing anything
  • lineTo(x, y) draws a straight segment from the current point to (x, y)
  • closePath() adds a line back to the sub-path's starting point and closes it
  • beginPath() clears all existing sub-paths so the next shape starts clean
  • stroke() outlines the current path; fill() paints its interior
  • A path can contain multiple sub-paths, each started by its own moveTo()
MethodPurpose
beginPath()Starts a new, empty path and resets the current point
moveTo(x, y)Moves the current point without drawing a line
lineTo(x, y)Draws a straight line from the current point to (x, y)
closePath()Draws a line back to the path's starting point and closes it
stroke()Outlines the current path using strokeStyle and lineWidth
fill()Fills the current path's interior using fillStyle

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="300" height="200" style="border:1px solid #ccc;"></canvas>

<script>
const ctx = document.getElementById('scene').getContext('2d');

ctx.beginPath();
ctx.moveTo(100, 20);
ctx.lineTo(140, 90);
ctx.lineTo(180, 40);
ctx.lineTo(160, 110);
ctx.lineTo(60, 110);
ctx.lineTo(40, 40);
ctx.lineTo(80, 90);
ctx.closePath();

ctx.fillStyle = 'rgba(147, 51, 234, 0.7)';
ctx.fill();
ctx.strokeStyle = '#4c1d95';
ctx.lineWidth = 2;
ctx.stroke();
</script>

</body>
</html>

Exercise: Canvas Shapes

Does the Canvas 2D API provide a built-in method to draw a triangle directly?