Dynamically Generate Histogram Images with Node.js and Canvas

As part of a new iOS app I've been working on I had the need to dynamically generate some images to represent some data. I have played a little with HTML5 canvas before, and I stumbled upon this project a while back so I decided it was time to give it a try to see if it would fullfill my need. In the end it ended up working out really well, I was able to throw the following proof of concept together in about 20 minutes (after about an hour of fighting through the install, there are a few pain points in Mountain Lion).

Start by installing node-canvas

npm install --save canvas

On Mac you may need to install Cairo

brew install cairo

Here is the code used to render my histogram

var http = require('http');
var Canvas = require('canvas');
var url = require('url');

var WIDTH = 30;
var HEIGHT = 60;
var DEFAULT_COLOR = '#58C6C2';

function render(canvas, numbers, color) { 
    ctx = canvas.getContext('2d');

    var biggestNumber = Math.max.apply(null, numbers);

    var currentX = 10 + WIDTH/2;
    var lineHeight = 0;

    ctx.moveTo(currentX, HEIGHT + 10);
    ctx.lineWidth = WIDTH;
    ctx.strokeStyle = color;

     numbers.forEach(function(it, ind) {
        lineHeight = (HEIGHT+10) - (HEIGHT * (it / biggestNumber));

        ctx.lineTo(currentX, lineHeight);
        currentX += WIDTH;
      ctx.moveTo(currentX, HEIGHT + 10);


So, you can see its really simple code, I just get a graphics context, then initialize some variables. Then I just create a path containing a line for every number I pass in. And one of the nice side effects of using node-canvas is that the above code will also run in the browser so I could generate my histograms client side or server side.

Now that I have my rendering code set up I need to create my web server. This is very basic, but it works for a proof of concept.

http.createServer(function (req, res) {
    var q = url.parse(req.url, true).query;
    try {
        var numbers = JSON.parse(q.numbers);
        var canvas = new Canvas(numbers.length*WIDTH + 20, HEIGHT+10);
        var color = q.color ? '#' + q.color : DEFAULT_COLOR;
        render( canvas, numbers, color );

      res.writeHead(200, { 'Content-Type': 'image/png' });
        canvas.toBuffer(function(err, buf){
    } catch (e) {
        res.end('Internal Server Error');
}).listen(8888, function() { console.log('Server started on port 8888'); });

That's pretty much it, I just write my canvas out to a buffer and then write the buffer to the client. Now I can use the following to call my service.


And I get:


I can also specify a different color…


Which looks like:


So this was pretty quick and dirty, but I think that this could prove very useful. You can see that there are all sorts of possibilities where this could be useful.

Check out the full source on GitHub. Thanks for reading.