Drawing Shapes with the JavaScript Canvas API


While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

In this article we’ll be looking at the HTML canvas element and the JavaScript canvas API to render complex shapes onto our web pages.


All we need to start is an HTML page with a canvas tag and a JavaScript file to manipulate it with.

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>HTML Canvas</title>


  <script src="./canvas.js"></script>

With our canvas element in place, we now need to create a new variable with it and a canvas context, which adds a bunch of functionality onto our canvas. To keep things simple we’ll stick with 2D shapes, but with the webgl context, 3D is also possible.

For our example we’ll need our canvas to be fullscreen but setting the size using CSS creates a strange blurry effect, which we obviously don’t want, so we’ll have to set it here.

// getting a reference to our HTML element
const canvas = document.querySelector('canvas')

// initiating 2D context on it
const c = canvas.getContext('2d')

addEventListener('resize', () => {
  canvas.width = innerWidth
  canvas.height = innerHeight


To draw rectangles, on our context variable (c), we can start adding what we want, measured in pixels:

  • rect(x-axis, y-axis, width, height): Sets the location and dimensions of our rectangle, and needs to be called before stroke or fill.
  • stroke: Renders an outline of everything before it.
  • fill: Renders the whole shape as a solid color.
  • strokeStyle and fillStyle: Sets the outline and shape color. They are not functions like the others and need to be assigned a string.
  • strokeRect and fillRect: Same as stroke and fill but only for that item, works the same as rect.
  • clearRect(x-axis, y-axis, width, height): Clears everything inside of a certain area. Very useful when we get into animations where we’re constantly rendering new elements and don’t want the old ones to stick around.
c.strokeStyle = 'white'
c.fillStyle = 'blue'
c.rect(100, 20, 150, 100)

c.fillStyle = 'red'
c.fillRect(400, 500, 300, 250)

// Uncomment to remove the first two blocks
// c.clearRect(0, 0, canvas.width, canvas.height)
c.fillStyle = 'green'
c.fillRect(1500, 500, 300, 250)


  • beginPath: Starts a new Line
  • stroke: Renders the line
  • moveTo(x-axis, y-axis): Sets the starting point
  • lineTo(x-axis, y-axis): Renders a line from the previous endpoint
  • lineWidth: Set the line’s thickness

And here are a few examples where we draw some lines:

// Just a basic line
c.moveTo(40, 250)
c.lineTo(200, 500)
c.strokeStyle = 'red'

// Draw the letter M
c.moveTo(1500, 700)
c.lineTo(1600, 450)
c.lineTo(1700, 700)
c.lineTo(1800, 450)
c.lineTo(1900, 700)
c.strokeStyle = 'blue'

// Let's now draw a house
c.lineWidth = 10
c.strokeStyle = 'red'
c.fillStyle = 'red'

// Walls 
c.strokeRect(800, 500, 300, 200)

// Door
c.fillRect(925, 600, 50, 100)

// Roof 
c.moveTo(700, 500)
c.lineTo(1200, 500)
c.lineTo(950, 300)
c.lineTo(700, 500)


The only method we really need for drawing circles is arc. The angles are taken in radians and not degrees so for our end-angle we can just use Math.PI * 2, since that’s equal to 360 degrees, and the starting angle can be left at 0. We’re not going to need to specify a value for counterclockwise, so we can just leave it off since it defaults to false.

  • arc(x, y, radius, starting-angle, end-angle, counterclockwise (boolean))
c.lineWidth = 5
c.arc(400, 400, 50, 0, Math.PI * 2)

Quadratic and Bezier Curves

If you’ve ever used graphic design tools like Photoshop or Affinity Designer, these will seem very similar to some of their line tools.

Essentially, quadratic and bezier curves are just free form lines with different methods of control. Quadratic curves are simpler in that they just have a start, endpoint, and what’s known as the control point, which acts as a handle for curving the line. You can see a wonderful interactive example here. Bezier curves, on the other hand, have two control points, at each end of the curve for more complex shapes. Another great example here.

  • quadraticCurveTo(controlPoint-x, controlPoint-y, endpoint-x, endpoint-y)
  • bezierCurveTo(startControlPoint-x, startControlPoint-y, endControlPoint-x, endControlPoint-y, endpoint-x, endpoint-y)

And some examples:

c.lineWidth = 5
c.strokeStyle = 'white'

c.moveTo(400, 400)
c.lineTo(400, 300)
c.quadraticCurveTo(450, 250, 500, 300)
c.lineTo(500, 400)

c.moveTo(800, 400);
c.bezierCurveTo(800, 150, 1200, 700, 1200, 400);


Text works very similarly to rectangles with a few CSS-like options for styling:

  • fillText(text, x, y)
  • strokeText(text, x, y)
  • font: Takes a string with the size in pixels and font family; like ’60px Times-New-Roman’.
  • textAlign: Takes a string with the same options as its CSS counterpart; start, end, left, right, and center.
c.font = '60px Times-New-Roman'
c.fillText("Hello World", 600, 500)
c.strokeText('Hello World', 1200, 500)


While there is still an enormous amount that can be done with HTML canvas like animations and interactivity, hopefully this was a good first introduction to some of its possibilities.

Creative Commons License