Building a Realtime Collaborative Microsoft Paint
Tags: javascript, tutorial, webdev, showdev
Published: 2020-02-12 10:41:27

---
title: Building a Realtime Collaborative Microsoft Paint
published: true
description: Building a Realtime Collaborative Microsoft Paint with JavaScript for the web.
tags: javascript ,tutorial, webdev, showdev
---
I built a thing, tweeted about it and promised to write about it. I didn't until now.

{% twitter 1142457275430461440 %}

In this tutorial, we’ll be building a realtime collaborative graphics editor to mimic the functionality of the iconic Microsoft Paint on Windows XP. We take the editors basic functionality to manipulate pixels and extend that to multiple users enabling realtime collaboration. Additionally, we want to be able to track the number of users working together on a project at a particular time, as well as create a medium for them to communicate via chat.

At the end of this tutorial, you should have an in-browser graphics editor that looks like this.
Try a [demo of it.](https://ms-paint-collab.netlify.com/)

![Collaborative Drawing](https://i.imgur.com/pgv06Zi.gif)

The aim of the project is to enable realtime collaboration, with possible use cases on distributed design teams or to classrooms. Powering our realtime collaboration is PubNub, it provides a secure, scalable and reliable realtime infrastructure to power any application through its global Data Stream Network. We'll use the [PubNub JavaScript SDK](https://www.pubnub.com/docs/web-javascript/pubnub-javascript-sdk) to connect multiple clients using the graphics editor. The goal is to have changes made on clients screens to reflect on all the others.

## Prerequisites for Building Collaborative Microsoft Paint
- Basic JavaScript knowledge
- [Node and Npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- A browser like Chrome, Firefox or Safari

To start out, we'll need to create a few files and folders. We need to create the **src** folder and make two new files - **main.css** and **app.js**, we'll get back to these a little later. We'll need a few image assets to make the graphics editor look appealing. In the root directory, create a folder called **images**, [download the image assets](https://www.dropbox.com/s/2vzy0lu1uygsy2u/images.zip?dl=1
) and paste them in the images folder. Now we’ll create an **index.html** file in the root directory and paste the following code:

{% gist https://gist.github.com/malgamves/dcb3f972120a7530ca4a05a03119822d %}

In **index.html**, `<header>` holds our applications menu bar with a *save* button that enables us to save our work locally. Right after the `<header>` we have a `<section>` that houses the controls of our editor, we'll only use the brush option. `<section>` is followed by a `<div>` tag with an id of *sketch* that houses our `<canvas>` with an id of *paint*, the `<canvas>` tag is important because it lets us draw graphics in the browser. Take note of ids for both because we'll need to reference them when adding functionality to our graphics editor using JavaScript. Another thing we need to take note of is the `<footer>` tag which houses a couple of `<div>` tags which will enable us to change the colour of our brush and draw with multiple colours. At the end of the `<footer>`, we have an `<h1>` tag that we'll use to keep track of the number of collaborators online.

To add styling to our app, we need to paste the following into **src > main.css**

{% gist https://gist.github.com/malgamves/b64bc31b705b61d553feeb8b7d322fed %}

Post styling, we need to add functionality to our application. To do so, we'll paste the following into **src > app.js** and run through what that code does.

{% gist https://gist.github.com/malgamves/7ca1e5624f878545a2acb1bb15b40809 %}

Our **app.js** file defines the functionality of our graphics editor. On the first line, we initialize PubNub and need to add our PubNub Publish and Subscribe keys. If you don't have a PubNub account, sign up for a free account to create your API keys. Sign up and log in using the form below:

After that, we define an object called *mspaint* that holds different properties. In the `start` property, we use a function that initializes our canvas by doing a few things:
- We set the context to '2d' as we will be only drawing in 2d
- We set the height and width of our canvas

Here, we use the `subscribe()` method to subscribe to a channel, *draw* which we defined earlier. We then define a callback function `drawFromStream()` which collects drawings done by other users and makes those drawings reflect on a users canvas. Next, we initialize [PubNub Presence](https://www.pubnub.com/products/presence/) to keep track of the number of users using the graphics editor at one time.

We also have a few event listeners that track when the mouse button goes up, moves and goes back down. The event listeners translate these movements to drawings on the canvas. When a user is done drawing, we want a user’s activity on their canvas to appear on the canvases of every other user they are collaborating with. We know that a user is done drawing when their mouse button goes back up. So we take advantage of the event listener that tracks when a users mouse button goes up. It is here where we add the `publish()` method from the PubNub JavaScript SDK so that a user’s drawing reflects on every other user’s canvas too.


To run our graphics editor, we need to serve the files locally. To do this, we'll have to install `http-server` by running `npm -g i http-server`. In our projects root directory, run `http-server` in your terminal and we have a working editor.

![Collaborative Drawing](https://i.imgur.com/pgv06Zi.gif)

We've got the realtime collaborative portion done, now we want to enable communication between collaborators via chat. The [PubNub Chat](https://www.pubnub.com/solutions/chat/) is a scalable and reliable In-App Messaging API that powers chat-based experiences.

We'll implement this by adding another item to our menu bar that opens up a modal containing the chat section. In the **index.html** file, add `<li><a href="#" id="openModal">Chat</a></li> ` to the `<header>` right after the last `<li>` tag. Now that we have the additional item in the menu bar, we need to paste this code that makes up our modal right after the closing `</header>` tag.
```
<!-- The Modal -->
      <div id="myModal" class="modal">
        <!-- Modal content -->
        <div class="modal-content">
          <span class="close">&times;</span>
          <p>Type your message and Press Enter.</p>
          <input id="input" placeholder="Your Message Here" />
          <p>Forum:</p>
          <p></p>
          <div id="box"></div>
        </div>
      </div>
```
The modal contains an input field where users can type messages they want to send to each other. The messages sent and received are displayed in the `<div>` tag with an ID of *box*, we take note of the ID because we use it to update the `<div>` with new messages in the chat. In **app.js**, the code below adds functionality to the modal.
```
let modal = document.getElementById("myModal");
let btn = document.getElementById("openModal");
let span = document.getElementsByClassName("close")[0];
btn.onclick = function() {
  modal.style.display = "block";
};

span.onclick = function() {
  modal.style.display = "none";
};

window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
};
```
What this code does is give us different ways to interact with and use the modal. Now that we have it working, we need to work on connecting different user’s chats to each other using PubNub. At the bottom of **app.js**, we have a function `chat()` that subscribes to chat message data in our collaborative graphics editor as well as a callback function - `publishMessages()` which publishes message data to the chat channel making all messages sent accessible for all the users of the chat in the graphics editor. We then initialize variables - *box* for the `<div>` tag that holds chat responses, *input* that handles data from the `<input>` tag and a channel where the communication occurs in PubNub’s Data Stream. We then subscribe to the channel using the `subscribe()` method and add a listener that updates the users with the latest messages from the chat by pushing them into our chat modal. We also have an `eventListener()` method to publish messages to the channel on a keypress. We then use the `onload()` method to make sure the chat functionality is enabled when the window loads. Run `http-server` and have a working chat to add to our collaborative graphics editor.



![PubNub Chat Demo](https://i.imgur.com/kMO6og7.gif)

Now we have a Realtime Collaborative Microsoft Paint site that can track the number of online users and connect them through a chat. If you have any questions, feel free to reach out to me on [Twitter](https://twitter.com/malgamves).

Sorry I took so long to release it. I hope you found it useful it.