Websocket and Data Visualization

Brian Greig
Frontend Weekly
Published in
6 min readAug 10, 2017

--

Rarely can you have a discussion with someone who is technically savvy and not get their perspective on where technology is headed. Right or wrong there is one thing that will alway a driver for these potential technological successes: they rely extensively on data. Case and point: the Internet of Things is dependent on steady streams of data to and from a user and their devices in order for it to work as intended.

When talking about the role data plays in technology there are two things to consider.

  • How is this data being communicated
  • How is the data being consumed (both by the user and by the machine).

We’ll be taking a look at these two things (for the latter we will be focusing more on the user than the machine).

Communication breakdown… It’s always the same

There have been many attempts over the years to create a solid method for communicating data over the web. AJAX, which is still used extensively today, was an early attempt at satisfying this by allowing users to poll data from the server. What it lacked was bi-directional communication between the client and the server. Rather than having the user continually poll the server for updates it has become increasingly necessary for the client and the server comminicate in both directions.

Enter the Websocket protocol in 2011. The Websocket Protocol (and the Websocket API in the browser) made is possible for servers to send messages to the browsers and for the front-end developers to enable listeners to assign behaviors based on those messages. Here is a short example showing how you can create messaging between a webpage in JavaScript and ws NPM module in Node (https://www.npmjs.com/package/ws)

//index.js (server)
const WebSocket = require('ws');
const wss = new WebSocket.Server({
port: 8080
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(d) {
console.log('received: %s', d);
});
});
//main.js (client)
const socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('open', function(event) {
socket.send("Some Data");
});

The above is a fairly trivial example just to show the basic implementation. We are going to build on this to create a basic application that retrieves data from a pseduo API in Node based on a ID and passes that data back to a webpage. This is going to the temperatures of the device (determined randomly) simulating a dashboard for monitoring the health of a device. The final dashboard will look like figure 1.

Figure 1: Our Dashboard

The graph above is a streaming visualization of the temperature of our device for the last 35 ticks. This will be achieved by posting our data in JSON format to a Websocket URL ws://localhost:8080. The data that is sent to the page will look like:

{
"DeviceID": "1A37F239BC1",
"State": "On",
"Temperature": 78.5
}

We will use a setInterval function on the Node server to pass this data:

var intervalID = setInterval(myCallback, 1000);function myCallback() {
getTemp(deviceId).then((t)=>{
ws.send(JSON.stringify({
"DeviceID": deviceId,
"State": deviceStatus,
"Temperature": t
}))
})
}

Save it for the Semantic Dome, E.B. White

So now that we have stream from the server it is important for us to communicate it back to our user. There are two ways that this can be done. One is by showing a snapshot in time. The other is a trend showing the information over a set period of time or over a number of available data points.

We’ll start with the snapshot. This simply presents the current state of the device as to not overwhelm the user. A semantic templating language is great for this type of presentation. Here is an example using Handlebars:

index.html (client)<div id="entry" class="panel-body">
</div>
<script id="deviceProperties" type="text/x-handlebars-template">
<div>
Device ID: {{deviceId}} <br>
Status: {{state}} </br>
Temperature: {{temp}}
</div>
</script>
main.js (client)// Retrieve the template data from the HTML (jQuery is used here).
var template = $('#deviceProperties').html();
// Compile the template data into a function
var templateScript = Handlebars.compile(template);
// Listen for messages
socket.addEventListener('message', function(event) {
var d = JSON.parse(event.data)
var context = {
'deviceId': d.DeviceID,
'state': d.State,
'temp': d.Temperature
};
var html = templateScript(context);
$("#entry").html(html);
});

A few things to note here. Within the index.html we create our semantic template using handlebars embedded expressions {{}}. This allows us to dynamically update the template when new data arrives via our EventListener. The template has an id of deviceProperties which we will reference in our JavaScript when we define our template. The div where our content will be placed has an ID of entry which we will use to target our rendered HTML.

In the JavaScript we will compile our template and we will add an event listener to fire every time we get a response from the server with a new object. We will parse out the JSON, assign it to a context, and load that contextualized data into our compiled template.

And now… the eye candy

Often times people need to see something visually to make clear sense of it. This is especially true with data. Presenting a series of data points in a table can be extremely hard for someone to interpret. So we turn to graphs, charts, and other visualizations to give a clearer picture to the user of what it means. In this case we are going to show a time series with every value represented on a bar graph for the last 35 ticks.

We will be using D3 for the presentation so if you are not familiar with D3 refer to the links in the appendix as it can be a little jarring at first. In a nutshell D3 takes a data structure and aligns it to a set of elements on the page. You can then use the values from each iteration to set the respective attribute values of the elements on the page. This can be used to create patterns, charts, and graphs.

var tempData = [];var svg = d3.select("#graph").append("svg")
.attr("height", 200)
.attr("width", 500)
// Listen for messages
socket.addEventListener('message', function(event) { if (d.Temperature != 'N/A') {
tempData.push(d.Temperature);
if (tempData.length > 35)
tempdata = tempData.splice(0, 1);
render();
}

Effectively what we had done is created a data structure (in this case an array) called tempData which is where we will store the last 35 ticks received from our server. We create a SVG which will be used by our render function to display our visualization. Then, d within the event listener for our message, we push the temperature value from the JSON into our array and popping the first value from the stack when the array length is greater than 35. Lastly we call our render function which draws the bar graph to the SVG element.

function render() {
//init
var rects = svg.selectAll("rect").data(tempData);
//enter
rects.enter().append("rect")
.attr("width", 5)
.attr("x", function(d, i) {
return i * 6;
})
.attr("y", function(d) {
return 100 - d;
})
.attr("height", function(d) {
return d;
})
// update
rects.transition().
duration(0)
.attr("x", function(d, i) {
return i * 6;
})
.attr("y", function(d) {
return 100 - d;
})
.attr("height", function(d) {
return d;
})
// exit
rects.exit().remove();
}

The render function has four steps: init, enter, update, and exit. Init is where we bind our data points to the elements of our SVG. Enter is where we assign them to the attributes on their respective elements. Update is to transition the graph to reflect the new information(specifically when we pop values off the stack), and exit is our cleanup.

Pulling it all together

Once everything is complete you have a fully functional dashboard. You can even add a button to toggle the feed on and off:

document.querySelector('#toggleState').addEventListener('click', function() {
socket.send("1A37F239BC1");
})

When you think about the application of this type of service it immediately becomes clear the value there is in being able to pump data into a website in realtime and to be able to present it back to the user in a visual manner that gives them the depth of understanding to monitor and make decisions. Be sure to grab the source code from my GitHub repo from the appendix below and experiment.

Appendix

https://developer.mozilla.org/en-US/docs/Web/API/WebSocket

--

--

Brian Greig
Frontend Weekly

Web designer, developer, business intelligence specialist, and all around nerdy tech guy.