Skip to content

Whiteboarding with LiveSwitch

Matthew Boshoff Oct 19, 2023 2:20:20 PM

In this article, we provide a detailed guide on creating an interactive whiteboard that supports multiple users at the same time. Using LiveSwitch, we can utilize a simple whiteboard application and give it remote capabilities. This guide will cover implementing a JavaScript whiteboard with LiveSwitch’s JavaScript libraries on a simple web page. If you are new to LiveSwitch, I highly recommend going over our introduction found here to better understand the fundamentals of LiveSwitch before you continue.

Basic Overview

Let’s create a simple whiteboard application using the third-party JavaScript Vector library Raphaël. Once we have a whiteboard set up on our page, we can use LiveSwitch’s channel messages to transfer information between all of our whiteboard users, thus making our whiteboard interactive for multiple users at the same time from anywhere around the world!

 

The Whiteboard

We can insert our Raphaël library inside the header of our page like below:

<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js" integrity="sha512-tBzZQxySO5q5lqwLWfu8Q+o4VkTcRGOeQGVQ0ueJga4A1RKuzmAu5HXDOXLEjpbKyV7ow9ympVoa6wZLEzRzDg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Once we have our libraries added, we can start creating our whiteboard. We will need a placeholder for where we will be drawing. Let’s use a simple div like below.

<div id="whiteboard"></div>

Now that we have a placeholder for our whiteboard, we can initialize it using Raphaël once the document has loaded. We will also want to keep track of some other values, so we will create some extra variables to track them. These will be our placeholders for tools, colors, and mouse positions.

var board = Raphael('whiteboard');
var line = null;
var path = null;
var color = null;
var size = null;
var selectedColor = 'mosaic';
var selectedSize = 4;
var selectedTool = 'pencil';

We have now initialized our whiteboard, but we are still missing some important functionality before we can actually interact with our board. We will need to implement the below methods so we can actually track what is being drawn onto our board. 

We will need a method to track our exact mouse position on the board:

var position = function(e) {

  var offset = $('#whiteboard').offset();
  return {
      x: e.pageX - offset.left,
      y: e.pageY - offset.top
  };
};

We will then need a method to track our different movements with the mouse:

var mouseMove = function(e) {

  var pos = position(e);
  var x = path[0][1];
  var y = path[0][2];
  var dx = (pos.x - x);
  var dy = (pos.y - y);
  switch(selectedTool){
    case 'pencil':
      path.push(['L', pos.x, pos.y]);
      break;
    case 'rectangle':
      path[1] = ['L', x + dx, y ];
      path[2] = ['L', x + dx, y + dy];
      path[3] = ['L', x , y + dy];
      path[4] = ['L', x , y ];
      path[5] = ['L', x, y ];
      break;
    case 'line':
      path[1] = ['L', pos.x, pos.y];
      break;
    case 'circle':
      path[1] = ['A', (dx / 2), (dy / 2), 0, 1, 0, pos.x, pos.y];
      path[2] = ['A', (dx / 2), (dy / 2), 0, 0, 0, x, y];
      break;
    case 'eraser':
      path.push(['L',pos.x, pos.y]);
      break;
  };
  line.attr({ path: path });
};

Now that we can track where our mouse pointer is and where it is moving along, we can add some more to the equation. We now want to bind some mouse events to our whiteboard and begin drawing. We should also add the ability to clear our board when needed. To do this, we will use the below methods:

//chrome has a repaint bug, this will resolve that issue

var forcePaint = function(){
  window.setTimeout(function(){
    var rect = board.rect(-99, -99, board.width + 99, board.height + 99).attr({stroke: "none"});
    setTimeout(function() {rect.remove();});
  },1);
};

var draw = function(path, color, size) {
  var result = board.path(path);
  result.attr({ stroke: color, 'stroke-width': size, 'stroke-linecap': 'round' });
  forcePaint();
  return result;
};

var clear = function() {
  return board.clear();
};

$('#whiteboard').mousedown(function(e) {
  var pos = position(e);
  path = [['M', pos.x, pos.y]];
  color = (selectedTool == 'eraser' ? '#000' : (selectedColor == 'mosaic') ? Raphael.getColor() : selectedColor);
  size = selectedSize;
  line = draw(path, color, size);
  $('#whiteboard').bind('mousemove touchmove', mouseMove); }
);

$(document).mouseup(function(e) {
  $('#whiteboard').unbind('mousemove', mouseMove);
  //TODO: We will want to send this information over LiveSwitch to our other users.
});

We now have a basic whiteboard that we can draw on and interact with. We have left out the parts that involve all the button interactions, changing your drawing method, and styling of the page altogether. We will leave that to your own discretion. There is just one piece missing: We need to make this multiplayer! Using LiveSwitch, we can now take all the positions we have been tracking with each movement/drawing, and we can transfer that information over LiveSwitch to another user so that they can see what we draw and vice versa! 

 

Adding Multiplayer!

Let’s take a look at what we will need to make this whiteboard interactive and live between multiple users. First, we need to add the LiveSwitch JavaScript libraries to our project; these can be downloaded from your LiveSwitch account. Once you have added the libraries to your project, we will need to follow a simple registration process to finally put all the pieces together.


Below is our simple client registration and channel connection. In this example, we are using the same channel for anyone who uses this whiteboard, but you can always add another layer to this by having separate channel names for different groups of users to allow private whiteboard sessions among specific groups.  

var applicationId = "YOUR-APP-ID";

var channelId = "whiteboard";
var secret = "YOUR-SECRET-KEY";
var client = new fm.liveswitch.Client("https://cloud.liveswitch.io/", applicationId);
var token = client.generateRegisterToken(secret, channelId)
var channel = null

client.register(token).then(function(channels) {
  channel = channels[0]
  channel.addOnMessage(function(sender, message){
    if(sender.getId() == client.getId()){
      // ignore self
      return;
    }
    var data = JSON.parse(message)
    console.log(data)
    if (data.clear) {
      clear();
    } else {
      draw(data.path, data.color, data.size)
    }
  })
}).fail(function(ex) {
  console.error("registration failed", ex)
});

There is a lot going on in the above code block. Let’s break it down to understand it better. We have our application ID, channel ID, and shared secret; we also have some global variables to keep track of our channel. We will initialize a LiveSwitch client so we can talk to the servers. Once we have a client initialized, we will need to generate a token to register our connection to the servers and choose our channel we want to connect to. This all happens in the generateRegisterToken method. We use our token to register our client to the servers. Once we have registered our client, we can utilize the channel that has been created and returned to hook into the addOnMessage function. 

 

The addOnMessage function allows us to send messages between our channels; this is all done without needing to create any audio or video connections to the channel. Now when we receive a message from another user, we can check if the user was either clearing the board or attempting to draw on the board. Based on that information, we can either clear our board or take all the position data in the message and draw their drawing onto our board. 


While this is all perfect for receiving messages and drawing onto our board, we will now need to implement the actual sending of data or clearing to the other users. To accomplish this, we will make use of some of our existing methods and add the sendMessage function of the channel object into those methods. Let’s take a look below:

$(document).mouseup(function(e) {

  $('#whiteboard').unbind('mousemove', mouseMove);
  if (line) {
    channel.sendMessage(JSON.stringify({ path: path, color: color, size: size }))
    line = null;
  }
});

$('.clear').click(function() {
  if(confirm('This will clear the LiveBoard. Are you sure?')){
    clear();
    channel.sendMessage(JSON.stringify({ clear: true }))
  }
});

As you can see above, where we had previously left our TODOs, we now have added the channel.sendMessage function. This allows us to notify the other users in the channel about what we are doing to our whiteboard, so it can reflect onto theirs. We have kept track of where we are drawing on the board, and once the user lifts up the mouse button, we can send that dataset over LiveSwitch to be absorbed by the other users and displayed on their board.

 

Congratulations, you have now created an interactive LIVE whiteboard! This is just a simple idea showcasing the power that LiveSwitch can unlock for you with real-time communication, whether that be video, audio, or simple data transfer. The possibilities of what you can create are only limited by your imagination. Happy whiteboarding!

 

Need assistance in architecting the perfect WebRTC application? Let our team help out! Get in touch with us today!