Making A Multiplayer Canvas With Node.js

Introduction

In this tutorial we will be making first a web page with a canvas on which you can draw. After that we will setup server with Node.js in order to let players communicate. To see this in action visit Canvas Land.

Setting Up The Singleplayer Project

Create a folder for the project. In it, make 2 files: The first is called index.html (full name with the extension. Don't make index.html.txt). This will be the page that will be displayed to the user. The second file is main.js (not main.js.txt). It is the JavaScript file that will do the logic of drawing.

The HTML File

First, start with the most basic html file. You can copy from below or, if you are on vs code, type "!" and it will auto complete:

You can change the title from "Document" to whatever you like. Next thing is to create a canvas element in the body of the page. This element will display the drawing. Give it an id so we could use it later in the code:

After that activate the main.js script using a script element:


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

Set Up The Canvas In Code

To get a reference to the canvas element use the getElementById function. In main.js :

There is a type of object we need from the canvas called a context. It has the drawing functions. For this project we need the 2d context:

Let's make the canvas fit the window. Set it's position to 0 0 and it's size to the size of the window:

Drawing A Line

Make a function in this JavaScript file called makeLine. For it accepts starting and ending x and y positions, width and color. For now, it would just draw a line (we will change it for multiplayer later). Just to see how it works call it once:

Variables

We need some variables that we would be able to pass to this function: mouseX and mouseY (The end point of the line). previousMouseX and previousMouseY (The start point of the line). isMouseDown (A boolean that tells us if the user is holding the mouse button down). brushSize (The width of the line). brushColor (The color of the line). It's comon practice to define the variables at the top of the file:

I decided to generate a random rgb for the color. Each of the values red, green and blue can get a value between 0 and 255.

Responding To Mouse Input

We will set up event listeners to change the variables concerning the mouse. First up, mousedown:

addEventListener means adding a function that will be called every time the event is triggered. In this case, when the mouse button is held down, isMouseDown turns true. Next, you might be able to guess, we do the opposite for mouseup:

Next up, mousemove. When the event is triggered the listener is called with an argument that has the information about the position of the mouse. clientX is the x position and clientY is the y position. We set the mouseX and mouseY accordingly but before that we set previousMouseX to mouseX so it's actually the value from before. The same goes for y:

Javaspript has this convenient function requestAnimationFrame which calls a function in some delay. We are not using it here for an animation but to draw if the mouse button is held down. Make it call the first frame when the page has finished loading:

Now add an if statement so that every frame, if the mouse button is held down, draw with the variables that we defined earlier (Don't forget to delete that test call of makeLine):

Setting Up The Node Project

First thing, if you don't have npm (node package manager) you need to download it. Create a folder with the name you would like for the game. Don't put spaces in the forlders name though. Open the terminal in that folder and type:
npm init
Fill in the ditails in the terminal (package name, description and such). Entry point is the JavaScript file that starts the app. I'm naming my entry point app.js . A package.json file is created.

Adding Dependencies

The node package manager (npm) allows you to add dependencies to your project. Those are libraries that include functions you may need. To add a dependency the syntax is:
npm i --save [dependency name here]
You write this in the terminal. The first dependency we need is called express. Type this in the terminal:
npm i --save express

App.js

Create a file for the entry point. I call mine app.js but you should use the name you chose when setting up. In node, to use a module you call a function called require. So to use express write in app.js:

When the node app servers a web page, the page can access resources from the server, like javascript files and images. To allow this, we define a specific folder to be publicly accessible. We will use the path module for that:

To server web pages we need to choose a view engine. There are a few options but my preferred choice is ejs. To add ejs as a dependency:
npm i --save ejs
Next, we define the folder for the views and the view engine:

Let's make it so the root url serves the game page:

To set up the server, a port needs to be selected. When running this app locally you can set the port to pretty much anything between 5000 and 8000. When this is hosted on some cloud service (like heroku in the case of the site you are looking at) a port is going to be supplied as an environment variable. So we say, if that variable is defined use it, else, use 8000:

Next, create a server and have it listen at the port:

The last three lines are to show in the terminal when the server starts running.

View And JavaScript Files

Remember when we defined the views folder? Now it's time to actually make that folder. Create a new folder in the project folder and name it "views". Inside that folder make a new file and name it index.ejs . Index is the name that we chose for the file above and ejs is the view engine. Copy the contents of index.html from the single player version you made earlier and paste it in index.ejs. There is one modification for this file that needs to be added, the src of the script main.js should now be "/main.js" rather than "main.js". The "/" points at the public folder we defined earlier.

Create a new folder in the project folder and name it "public". Copy main.js and paste it in that folder.

Start the server

In the project folder there is a file named "package.json". Most of what we need is already there but the start script isn't. So inside the scripts section add the start script. I'll just Show you my package.json here:

To start the server type in the terminal:
npm start
To visit the page you just created go to http://localhost:8000. The game is now served by the node app but still single player. Close the server by pressing ctrl + c on in the terminal.

Adding Multiplayer

To create real time multiplayer we will use a module called socket.io . To add it type:
npm i --save socket.io
Set socket.io to listen to the server:

Each client is represented by a socket. The client can send an even to the server with any name, with any data. In this case the client is going to send an event named "add-line". When the server accepts the the event it sends event "line-added" to the other sockets so the lines would be drawn on their pages:

In order to use the client side of socket.io you need to add it to the page itself, index.ejs:


        <script src = "/socket.io/socket.io.js"></script>
        <script src = "main.js"></script>

In main.js define the socket (at the top of the file):

Change the makeLine function to send the add-line even and move the drawing code to a new function, drawLine:

On the line-added call the drawLine function:

npm start again and open multiple tabs on localhost:8000 and draw to see the multiplayer functionality.

Right now when a new player joins they do not see the lines that were drawn before. To fix this, make a list to which new lines will be added. When a new socket connects the list of lines will be sent to it. in app.js:

In main.js, add a listener for the "lines-added" (Not the same as "line added"). On that event, each line will be drawn:

The app now runs locally on your computer. Other people can't access the port because it's not open to the internet. It's possible to open it but I really don't suggest you do that. Instead, you can use a hosting service to run the app on a remote machine. My app is hosted on heroku where you can go for a free plan.

Thanks for reading. Happy drawing ❤️