The inner life of heatmap.js

Estimated reading time: 5 minutes ( save for later )

tl;dr – Instant summary

A wise guy once said “A picture says more than thousand words” so I try to summarize the inner life of the heatmap.js library in the following illustration (If you don’t understand the graphic, you better read the article ;) )
heatmap.js description

A heatmap instance contains a store. When data gets added to the store, (store[x][y] =count) the store checks if there is a new maximum count and then calls the drawAlpha function in order to display the datapoint on the heatmap (if there is a new maximum count, it will redraw all datapoints).
The alphamap then gets colorized based on a custom gradient’s image data.

Introduction

Okay maybe you saw my previous realtime heatmap examples where I released a single page and you were able to see a heatmap based on your mouse position. They were pretty cool examples, I’m glad I was able to inspire other people to use my code and create awesome stuff (e.g. some students at the MIT created a really neat project where they show the density of smiling people on the MIT campus, or a multi-user click-map, etc). But to be honest, the scripts I’ve released had a lack of a major criteria: data-management. In this article I’ll explain the new concepts and what happens when you use heatmap.js.

Rethinking concepts

So I finally found enough time to take another approach on my heatmap script, I always knew data management had to be implemented, sooner or later but when I released the first versions of the script I just thought “uhm data integration? too much effort – too less time” – postponed. The first and most important step in order to improve the script was to question my previous concepts – they worked, but are they really good? Apparently they weren’t that good, here are some thoughts about the old concepts:

Gradient calculation

I think this was the worst part of the old heatmap script, though it was an really interesting idea. To summarize: The point was to take an alpha value from 0 to 255 and calculate red, green and blue values based on this value. Every single pixel ran through three or more conditions in which then the rgb values got calculated. The result was a fixed heatmap gradient because finding a calculation model for another color gradient was just not possible (at least for me. Okay I have no degree in Maths [yet] but I know what I’m doing).

The module pattern

Since the first version of the heatmap script was only for testing a single heatmap on webpages the module pattern wasn’t that bad for generating the realtime heatmap. But if you want to have several heatmap instances on a single page, heatmaps had to be constructable objects in order to have different configurations.

What stayed?

The alpha map

The generated heatmaps are still based on the alpha map concept. It contains a layer where radial gradients from black to transparent get painted. Based on the alpha data of the radial gradients (the gradient’s alpha starts at a calculated value in relation to the datapoint count and the maximum count) the heatmap (it has another [visible] layer, because we want to set the opacity of heatmap layers too) will be colored.

What’s new?

Gradient initialization

With this concept it’s possible to customize the heatmap’s color gradient. In order to make this possible heatmap.js creates a 1×256 pixel canvas on it’s initialization and draws a linear gradient with n specified color stops. Then it stores the image data in an array, a pixel is defined by it’s red, green, blue and alpha value which means the array contains 1024 (256*4) values. Thus when the heatmap is colorizing based on the alpha map’s alpha value it simply has to access the image data array on index alpha*4 (+1, +2) and take the values as red, green, blue;
It is much easier and more efficient than the previously explained gradient color calculation since it is customizable, has no calculations/conditions to pass, it’s just accessing 3 array elements.

The heatmap factory

The heatmap factory is basically an implemented module pattern containing the object constructors of the heatmap and the heatmap’s store. The result is an object with the function “create” – This create function instanciates new heatmaps and returns them.

Integrated data management

As mentioned before the heatmap factory contains an object constructor for the heatmap’s store. A store contains a reference to the heatmap itself, a data attribute (not public accessible) and a max attribute which represents the maximum count value in the data. The data field is a two dimensional array, the first dimension represents x-values and the second dimension y-values therefore the count for a datapoint is stored in data[x][y]. But why does the store have a heatmap reference? Because the store triggers the drawing process for each datapoint. When you add a new datapoint to the heatmap’s store, the store is checking if there is an index for the new datapoint, if not, it creates one and then checks whether there is a new maximum or not. If there is a new maximum count, the store initiates a global redraw by cleaning the heatmap and then redrawing each datapoint. My first thought on that approach was “This will never be fast enough. Redrawing all datapoints when there’s a new maximum? I mean wtf?” but it turned out that it works pretty nice and therefore the heatmaps now work with [real] data. :-)

Summing up

All in all I’m glad that I took the next step in this project. Now, heatmap.js is actually a useful project and there are lots of usecases for it :) If you build something great with heatmap.js, please let me know! Speaking of building great things with heatmap.js, I’ll releaseI’ve released a new demo – a realtime webtraffic heatmap soon – and of course I will write an article about it, explaining the key hooks – and in this case, if there is enough demand I’ll write a “How to setup your own realtime website traffic heatmap” article ;) Hope you enjoy heatmap.js. ~ Patrick