Building a Browser Game Part 1: Rendering to a Canvas

Photo by Sigmund on Unsplash

Who doesn’t love video games? Well, lots of folks…but as a fledgling developer, building a video game is quite an exciting, if a bit daunting prospect. Still, by breaking it down into manageable chunks, let's see if we can’t get something working. I aim to look into the first few steps of making a browser game using only HTML, CSS, and JavaScript. It's gonna be messy, it's gonna take some research, but at the end of the day, if we’re pernicious enough, it just might work. Join me with the first topic: Understanding the basics of canvas elements in Html.

First, let’s set up all of our files. We can keep the HTML pretty dang simple at this point. Once we’ve set up our <head> and linked our script.js and style.css files, all we need to add for now is a <canvas> element in our body. We’ll give it an ID of “canvas.”

<body>
<canvas id="canvas"></canvas>
</body>

The canvas element is a pretty versatile node in HTML. It is typically used for animations, graphics, data visualization, photos, and a wide range of fancy visual effects. We’re going to use it as the space to render our game. This means that we will have to find a way to make changes to the canvas from our JavaScript file. In our scipt.js file, we’ll start by grabbing the canvas element and then getting its context.

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

The getContext() method returns the context (often abbreviated to “ctx”) and will be the object on which we call all of our different drawing functions. Think of it as the place where we render everything. We specify “2d” context because we’ll be sticking to a two-dimensional game. Canvas context can be pretty versatile, however. You can use it to render in 3d or put your computer’s GPU to work with shaders using WebGL. But let's just worry about 2d context for now.

Let's try drawing something on our canvas. Add the following two lines:

ctx.fillStyle = "salmon";
ctx.fillRect(10, 10, 150, 100);

You should now see something like this:

A Salmon colored recrangle rendered on an HTML canvas element.
A Salmon colored recrangle rendered on an HTML canvas element.

What are those two lines of code doing? First, we are setting the color we’d like to use when we render our drawing. To set a color, we’ll always use one of two properties, fillStyle, and strokeStyle. FillStyle is used to fill in the shape that we draw while strokeStyle will be used while drawing lines and outlines. We can set the style to any valid DOM color strings (HEX, rgb(), rgba()) or even pattern or gradient objects!

FillRect() is a function that takes 4 arguments and draws a solid rectangle given the current fillStyle of the context. The arguments are as follows: fillRect(top-left x coordinate, top-left y coordinate, width, height). Canvas elements work in an (x, y) coordinate system. By default, the top left corner of the canvas is (0,0) and each pixel of the canvas corresponds to a coordinate position. Try changing the arguments in the fillRect() method, see how it changes the rectangle.

Let’s try something else. Add the following below our previous code.

ctx.strokeStyle = "#36fff8";
ctx.beginPath();
ctx.arc(85, 60, 30, 0, 2 * Math.PI);
ctx.stroke()
A Salmon colored rectangle with a teal circle drawn within.
A Salmon colored rectangle with a teal circle drawn within.

This time, we did a little bit more complicated. First, we set the strokeStyle to a string containing a HEX color. Then we started our path with beginPath(). A path is just a list of positions connected with straight or curved line segments. Then we used the arc() function which takes in center x coordiante, center y coordinate, radius, starting angle (in radians), and ending angle (in radians). Finally, we use the stroke() method to draw our path onto the canvas. Try playing with the values in strokeStyle and arc() to see how they change the path. Maybe throw in a ctx.lineWidth = 10 sometime before stroke() is called, see what happens. There are a variety of methods that you can use to add and change points along the path to achieve more complicated shapes. You can look into bezierCurveTo() if you're feeling real fancy.

Notice how our circle was drawn on top of our rectangle? If you switch the order of our two sections of code, you’ll no longer see the circle at all. It’ll be important to keep in mind that the draw order will matter moving forward.

Canvases can be used to make some extremely intricate and impressive visuals. Check out Micha Elizabeth Scott’s Zen Photo Garden and just marvel at how cool it is. And then browse through the MDN 2D Context Documentation to get a feel for all 2d context methods you can use to draw to your heart’s content.

In the next part, we’ll be looking at how to create a simple game/animation loop to add some movement into the picture.

Here is CodePen to see all the code talked about above.