Build a real-time whiteboard application with Azure Web PubSub
Tags: webdev, azure, javascript
Published: 2021-07-07 09:22:28

The COVID-19 pandemic has led to unprecedented measures and is also changing the way we work. In addition to restricting travel and canceling large events, a growing number of companies are encouraging to work remotely. In order to continue working efficiently and creating value under these new circumstances, organizations need to adopt different applications with different scenarios, e.g., web conference, remote collaboration, etc. The real-time whiteboard is one of the tools to help you build remote collaboration and bring your teams together, anytime, anywhere, e.g., running productive and engaging online meetings and workshops, building and developing ideas with distributed teams, explaining complex process and system with visual map or diagram, etc. The Azure Web PubSub (AWPS) which is a fully managed service could help you build the real-time whiteboard application. Let’s learn how to build a whiteboard demo together.

## What’s the whiteboard demo?  

This whiteboard demo demonstrates how to build a web application for real time collaboration using Azure and other related technologies. The fundamental feature of this application is allowing anyone painting on it and synchronizing the paint to others in real-time. The user could paint with the basic paint tool, touching on mobile devices or uploading images.

![Real-time Whiteboard Application](https://techcommunity.microsoft.com/t5/image/serverpage/image-id/294911i87AB250939F9360A/image-size/large?v=v2&px=999)

Before we start digging into details, you can first play with this demo online. Open this [site](https://awps-demo-whiteboard.azurewebsites.net/) and input your username, then draw anything you like in the whiteboard. Open another browser window you'll see your changes are synchronized in real-time.

You can also find the source code of this demo [here](https://github.com/Azure/azure-webpubsub/tree/main/samples/javascript/whiteboard).

## Synchronize real-time data between client applications

One of the key features in this whiteboard is its ability to synchronize the drawing between multiple client apps in real-time. This is implemented by using [WebSocket](https://en.wikipedia.org/wiki/WebSocket) technology, which is commonly used in web applications for bidirectional communication. If you're already familiar with WebSocket and look into the implementation in [server.js](https://github.com/Azure/azure-webpubsub/blob/main/samples/javascript/whiteboard/server.js), you'll notice it's very different than a typical WebSocket server. In a typical WebSocket application, server needs to manage all client connections and handle data transfer between clients. So you can imagine in a whiteboard app, server will receive the drawing from one client and broadcast it to all other clients, which will generate huge traffic when everyone is drawing and processing all those data will be a big burden to the server.

If you look at our whiteboard server, you'll see it's a standard [express.js](https://expressjs.com/) server application (we don't go through details about how to use express.js here, you can refer to its official docs to learn more). Instead of having code to handle WebSocket connections, it creates a `WebPubSubEventHandler` and use it as a middleware in the express app.

```javascript
let handler = new WebPubSubEventHandler(hubName, ['*'], {
  path: '/eventhandler',
  handleConnect: ...
  onConnected: ...
  onDisconnected: ...
  handleUserEvent: ...
});
app.use(handler.getMiddleware());
```

By using this Web PubSub event handler, we're leveraging [Azure Web PubSub](https://azure.microsoft.com/en-us/services/web-pubsub/) service to manage the client connections for us.

In the event handler there're some callbacks like `onConnected` and `onDisconnected`, which are similar to `open` and `close` events in WebSocket, but the key difference here is when using Azure Web PubSub service, the connection is connected to the service, your server just gets a notification when this happens but does not need to manage the lifetime of the connection. This is usually challenging in real scenarios where you need to handle things like connection routing and load balancing. In Azure Web PubSub they're all taken care of by the service.

Also in the server code you'll notice there is no code to pass the data from one client to another, this is purely done at client side. Look at the [client](https://github.com/Azure/azure-webpubsub/blob/main/samples/javascript/whiteboard/src/websocketclient.js#L40) you'll see code like this:

```javascript
this._webSocket.send(JSON.stringify({
  type: 'sendToGroup',
  group: group,
  dataType: 'json',
  data: data
}));
```

This code sends a message to the WebSocket connection but there is no code at server side to handle it! This is because it is processed at service side. Azure Web PubSub can understand message from client (in this case it's asking service to send this message to a group) and send the message to the corresponding clients (which is called publish/subscribe pattern). So you can see by using Azure Web PubSub you can save a lot of server resources (like CPU and network bandwidth) by offloading WebSocket connections to the service.

## Maintain state at server side

Even Azure Web PubSub helps us deliver real-time updates between clients, client still needs to send drawing data to server so it can be saved at server side. So next time when a new user opens the whiteboard, they can see all paintings others draw before.

This is done by sending an event from client to server. Event is another communication pattern (comparing to publish/subscribe pattern we use for sending real-time updates) in Azure Web PubSub for clients to send data to server.

In [client](https://github.com/Azure/azure-webpubsub/blob/main/samples/javascript/whiteboard/src/websocketclient.js#L31), there is code like this:

```javascript
this._webSocket.send(JSON.stringify({
  type: 'event',
  event: 'message',
  dataType: 'json',
  data: data
}));
```

This code sends an event to server and there is corresponding server [code](https://github.com/Azure/azure-webpubsub/blob/main/samples/javascript/whiteboard/server.js#L37) to handle it:

```javascript
let handler = new WebPubSubEventHandler(hubName, ['*'], {
  path: '/eventhandler',
  ...
  handleUserEvent: async (req, res) => {
    let message = req.data;
    switch (message.name) {
      case 'addShape': ...
      case 'removeShape': ...
      case 'clear': ...
    }
    res.success();
  }
});
```

You can see the code above handles three types of events (when user adds a new shape, removes an existing shape or clears the whiteboard) and save the data to a local `diagram` object (for demo purpose, in a real application you should use a persistent storage to store this diagram).

> You can see there is still data communication between client and server, but comparing to the real-time updates between clients (which happens whenever a user moves their mouse on the screen), this only happens when a user finishes drawing a shape, so the amount of data is much less than the real-time updates.

Besides user events, Azure Web PubSub also supports some system events like connected and disconnected so server can know the status of client connections. You can see in the server code, they're used to track the total number of clients online.

## Use WebSocket API in client

Azure Web PubSub uses [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) for its client programming interface. As long as your programming language supports WebSocket, you don't need to install any third-party library. You already see how to send messages through WebSocket in previous sections, but there are a few more things you need to be aware before using it:

### Authenticate with service

Azure Web PubSub doesn't support anonymous connection, so in order to connect to the service each client needs to authenticate with it using a [JWT](https://jwt.io/) token. Azure Web PubSub SDK already provides an API to generate the token from connection string. A recommended implementation is to expose a Web API (usually called negotiate) at server to return this token (the API itself can be protected by your own authentication mechanism). In the demo app it's implemented like this:

```javascript
app.get('/negotiate', async (req, res) => {
  let token = await serviceClient.getAuthenticationToken({
    roles: ['webpubsub.sendToGroup.draw']
  });
  res.json({
    url: token.url
  });
})
```

In the negotiate you can also control the permission of client (like which group it can send message to).

### Create connection

Create connection is really simple but you need to specify a subprotocol if you want to directly send messages between clients.

```javascript
let res = await fetch('/negotiate');
let url = res.json().url;
let ws = new WebSocket(url, 'json.webpubsub.azure.v1');
```

With `json.webpubsub.azure.v1` subprotocol, you'll be able to join, leave and publish messages from client (more details can be found [here](https://azure.github.io/azure-webpubsub/references/pubsub-websocket-subprotocol)).

> If you don't specify subprotocol you can still connect, but all messages you send will be treated as events and be sent to server.

### Handle reconnect

It's very common that WebSocket connection will drop due to things like network glitches, long time inactivity at client side, etc. So to improve the stability of the client app you should always consider reconnect when it disconnects.

In Javascript if a WebSocket connection is closed you need to create a new WebSocket object to reconnect, which means for all callbacks you registered on the old object you need to re-register in the new one. In this demo we created a simple WebSocketClient class to wrap the raw WebSocket object so it will automatically reconnect and re-register all callbacks. Check out the [source code](https://github.com/Azure/azure-webpubsub/blob/main/samples/javascript/whiteboard/src/websocketclient.js) to see how it works.

## Next Steps 

Now, we already go through the key points to build the real-time whiteboard application with Azure Web PubSub service. If you are looking for more details about this demo, you can refer to the [whiteboard application on Github](https://github.com/Azure/azure-webpubsub/tree/main/samples/javascript/whiteboard) where the code is hosted, along with information and docs on how to deploy and run it yourself.

If you are trying to build your first real-time application with Azure Web PubSub, you could also get more helpful resources from the [getting stared contents](https://azure.microsoft.com/services/web-pubsub/#documentation). We are looking forward your feedback and ideas to help us become better via [Azure Feedback Forum](https://feedback.azure.com/forums/934191-azure-web-pubsub-service)!

## More Information
This blog is also posted on [Tech Community](https://techcommunity.microsoft.com/t5/apps-on-azure/build-a-real-time-whiteboard-application-with-azure-web-pubsub/ba-p/2522265) and you could also get more blogs about the Web PubSub and its new features here.

At last, thanks for [Ken Chen](https://techcommunity.microsoft.com/t5/user/viewprofilepage/user-id/692206) offering the demo and technical details.