Spicy Yoghurt | Last updated: 9 March 2019 | HTML5 game tutorial

Draw on the HTML5 canvas

Draw graphics on the HTML5 canvas using shapes, paths and fills. Set-up an HTML5 file, apply scaling and perform drawing operations. By the end of this tutorial you can draw your own graphics on the canvas.

What is an HTML5 canvas?

When you want to draw your own image, animation or game with HTML5, you'll need a canvas. The <canvas> tag is new in HTML5. It's a container for graphics and you can draw on it by using JavaScript. You can draw paths and shapes, but also text and images.

It works like an <img> tag, only you can draw the image yourself instead of loading it from an external source. In fact, in modern browsers, you can even right click a canvas element and save the image. It's like a dynamic image. Paint on canvas The canvas will be the core of all the drawing operations for your game. It works like a drawing board. You can draw on it and clear it. But unlike drawing in real life, drawing on the canvas takes hardly any time. You can repeat the process of drawing and clearing so quickly that, if you would change the position of objects just a little bit each time before you draw, you'll get the perception of motion. This is the basic principle of animations and games. You change position, draw and clear, for hundreds of times in a row.

In this tutorial, you'll start of easy and learn about drawing a single static image to the canvas. Check out the canvas animation tutorial if you already know how to do that and want to draw dynamically straight away.

Set-up a basic HTML5 file

Before you can draw any kind of graphics, you'll have to set-up a basic HTML5 file. You'll have to tell the browser how to interpret the file and what kind of content it contains. Let's get started with the code below:

                    
                        <!DOCTYPE html>
                        <html lang="en">
                            <head>
                                <meta charset="UTF-8">

                                <title>HTML5 Game</title>
                                <meta name="description" content="A basic HTML5 game">
                                <meta name="author" content="[Your name]">
                            </head>
                            <body>

                                <!--Your canvas will come here -->

                                <script>
                                    //Your script will come here
                                </script>
                            </body>
                        </html>
                    
                    
Let's take a closer look:
  • The <!DOCTYPE> declaration tells the browsers what HTML version the page is written in. The HTML5 doctype is short and simple compared to older versions.
  • The <meta> tags and the <title> tag describe the content of the page. Search engines like Google use this information to build search results.
  • Inside the <body> tag is where all the actual content of the page will be stored. This is also where your canvas will be placed.
  • You can see two <script> tags right before the end of the <body> tags. Between those tags is where your program is going to start.
You can copy this code and store it in a file named index.html. This file can be run by any browser and will display your newly created HTML5 web page. Too bad it is still completely empty. Let's do something about that by adding a canvas.

Add a canvas element

Now directly after the <body> tags, add a <canvas> element. This is the place where the graphics of your game will be drawn later on.

            
                <canvas id="canvas" width="750" height="400" style="border:1px solid lightgrey;">
                    Your browser does not support the HTML5 canvas tag.
                </canvas>
            
            
When you try to use the canvas in a browser that has no canvas support, the fallback content will be shown. For now, it's just a simple message about lacking support, but you could make something fancier. Perhaps you could add a static image of what your canvas content would've been? Just place the fallback content between the <canvas> tags.

Scaling the canvas to fit on all screen sizes

When you refresh your page, you should see the border of the canvas. The element has its width and height properties set. That's to define the size of your canvas, your drawing board. Every drawing operation will be performed in this coordinate space.

You can scale the total canvas by using standard CSS to set a width and height percentage on the element. For example, applying width: 100% to the element would stretch the canvas to fit the maximum width. But all drawing operations will still be done within the coordinate space of the canvas. You'll never have to take the actual size of your canvas into account when drawing.

How is this helpful? Well, this enables you to work on a game with a set resolution, let's say 1280x768, and run this game on devices with bigger and smaller screens. While programming, an x-position of 1280 will always be the right edge of your screen even if the actual screen size of the current device is much smaller or larger than that. The scaling of the canvas takes care of that.

Nothing is drawn on the canvas yet. You need to get a reference with some JavaScript code to do that.

Get a reference to the canvas

To perform drawing operations, you'll need a decent reference in your code to your canvas. Expand the <script> tags with the following JavaScript code:

                    
                        <script>
                            "use strict";
                            var canvas;
                            var context;

                            window.onload = init;

                            function init(){
                                // Get a reference to the canvas
                                canvas = document.getElementById('canvas');
                                context = canvas.getContext('2d');

                                draw();
                            }
                        </script>
                    
                    
The most important steps are taken inside the init() function. In there the canvas is stored for later reference by calling getElementById() to retrieve the canvas element.

The canvas has no functions to draw on its own, you'll need a context for that. Call getContext() to retrieve the context from the canvas. '2d' is passed as argument to get a CanvasRenderingContext2D object. That's the context you need to draw 2D objects. There are also other context types like WebGL, for 3D, but that's not what we need right now.

Once the references are made, the function draw() is called. In there, the actual drawing operation will be performed. You'll implement the function in a bit.

JavaScript in strict mode

"use strict" is telling the browser to run the code in strict mode. This means your code will be validated more strictly. Things like using an undeclared variable are no longer allowed. This will force you to write cleaner code, which will prevent coding errors. It is an optional line, but it is recommended to leave it in. Learn more about use strict here.

How to make sure the element is loaded?

If you don't waiting for the page to load before referencing the canvas, you'll try to work with a canvas that isn't there. This will result in an error saying there is no canvas element.

To fix this, you can use the onload event. The window.onload event will trigger once the whole page is loaded. This will make sure all elements and resources on the page are available before you execute your code. The init() function is set as the event handler. Once the onload event fires, init() will be called and the canvas will be available for referencing.

Draw your first graphics

The last piece of the puzzle is the draw() function. The function looks like this:

                    
                    function draw(){

                        // Get a random color, red or blue
                        var randomColor = Math.random() > 0.5? '#ff8080' : '#0099b0';

                        // Draw a rectangle
                        context.fillStyle = randomColor;
                        context.fillRect(100, 50, 200, 175);
                    }
                    
                    
At first, a color is selected as fill style for the context by setting fillStyle. Then, the actual drawing operation is performed to draw a rectangle to the canvas, by calling fillRect(). The position and size of the rectangle are passed as arguments.

This is how most canvas operations work. You define a style and then fill. You could say you first define the brush you want to use and then start to paint. Also, notice how the paint color isn't passed into the fill function. It is stored in the context. This means that every next drawing operation will use the same paint as the current. Until you change the fill style of course.

Proof: it's a dynamic image

When you add the previous code to your index.html file, and run the file in the browser, you will actually see something getting draw on the canvas. It's a big rectangle! With the current code, the rectangle is either red or blue. This is because every time the page is loaded a new random color is picked in the draw() function, by using Math.random() to pick a color as fill style. You can try to refresh this page a couple of times and see the color getting changed.

The random color is used to demonstrate the fact that you are not looking at a static image. You are drawing your own graphics dynamically! You can modify the arguments inside the function to create rectangles of different sizes and colors.

Draw shapes with paths

The 2d-context only supports one primitive shape, and that's the rectangle. You could use this to draw a square, by making a rectangle with even sides. But if you want to draw anything else, like a triangle, circle or any other shape, you'll have to use a path.

Paths start by calling beginPath() on the context. You then define the shape of your path. This can be a circle or a line for example. After defining the path, you can call fill() to draw the path. This tutorial won't dive deep into paths but here are some quick examples to get the idea.

How to draw a circle?

Here's a piece of example code that draws a circle. You can see it starts with the beginPath() function and ends with fill().

                    
                    context.beginPath();
                    context.arc(200, 100, 50, 0, 2 * Math.PI);
                    context.fill();
                    
                    
The arc() function is the one who defines an arc-shaped path, in this case a full circle.
  • The first two arguments are the x and y of the center of the circle.
  • The third is the radius of the circle. A larger radius will make for a larger circle.
  • The last two arguments are the starting angle and ending angle of the arc in radians. This example circle starts at an angle of zero and ends at an angle of two times pie, making a perfect circle.
You can stack up multiple commands, like arc(), to create more complex shapes.

And what about a triangle?

By using multiple commands, you can draw up more complex shapes. This enables you to create a triangle existing of three lines. Here's an example on how to draw a triangle:

                    
                    context.beginPath();
                    context.moveTo(200, 100);
                    context.lineTo(250, 150);
                    context.lineTo(250, 50);
                    context.fill();
                    
                    
Again it starts with calling beginPath(). The next call, moveTo(), is telling the context where to start drawing the path. So, it doesn't really draw anything on its own, it only tells on which coordinates to start. From there you can draw lines with lineTo() to create the desired shape.

Notice how lineTo() is called only twice. But you needed three lines for a triangle, right? The last line is automatically drawn when you call fill(). It closes the path and connects start to end.

The difference between using a fill and stroke

You have been using fillRect() or fill() to draw on the canvas. But there is another option. Instead of filling, you can also stroke paths and shapes.

When stroking, you only draw the outline of an object. So the center can still be empty. You could also use it in combination with a fill to create more complex drawings. Here's a simple view on the difference between fill and stroke:
Fill and stroke explained

Stroke the outlines of paths and shapes

Here is an example of previously used shapes that are stroked and not filled.

                    
                        context.beginPath();
                        context.arc(200, 100, 50, 0, 2 * Math.PI);
                        context.strokeStyle = '#0099b0';
                        context.stroke();

                        context.beginPath();
                        context.moveTo(200, 100);
                        context.lineTo(250, 150);
                        context.lineTo(250, 50);
                        context.stroke();

                        context.strokeStyle = '#ff8080';
                        context.strokeRect(100, 50, 100, 125);
                    
                    
As you can see fill() and fillRect() have their counterparts stroke() and strokeRect(). There even is a strokeStyle attribute just like fillStyle.

If you want to create a thicker line for the stroke, you can use the lineWidth property:

                
    context.lineWidth = 5;
                
In the example, the circle is drawn in blue, then the triangle is drawn without specifying a specific color, and then the rectangle is drawn in red. Since the context stores the fill- and stroke style, the triangle is drawn in blue too, without specifying this specifically.

And there is another notable thing happening here. The triangle is not so much a real triangle. It is more like a rotated L-shape. That's because, unlike the fill() command, stroke() doesn't tie the begin of the path to the end. If you need a stroke of a triangle, you'll have to specify three lines, not two.

That's all for now on paths. If you like, you can experiment with different shapes and drawing operation to create graphics of your own. For this tutorial, you'll be using basic geometric shapes for the time being and switch to images later on.

Draw your own text

Aside from shapes and paths, you can also draw text on the canvas. This operation also works with a fill- and stroke style, just like any other drawing operation. To draw a simple line of text, use the fillText() function. It takes text and an x- and y- coordinate as arguments.

            context.fillStyle = 'black';
            context.fillText("Some text", 200, 100);
        
You can set the font and text alignment and -baseline on the context. The font property uses the same syntax as the css font property. The textAlign property is a bit tricky. It marks on which side the text is aligned with the given coordinates. So, a textAlign = 'right' will display the text left of the coordinates you gave up. Not right. The same is true for the textBaseline. A textBaseline = 'bottom' will display the text above the baseline.

            context.font = '25px Arial';
            context.textAlign = 'right';
            context.textBaseline = 'bottom';
        
Here's an example of text drawn on the canvas: That's all for now on drawing operations. Try to mix them up to create new kinds of graphics.

Final code

You finished this tutorial and have your own HTML5 canvas ready to use. You can perform your own drawing operations now. You can download the final code here. Feel free to ask questions in the comments.

Of course, this is only just the start. In the next step of the tutorial, you will learn how to do multiple drawing operations in a row. You will create a game loop!

Leave a comment