Deep in my heart, when I need to visualize data, I generally prefer D3.js. It allows you to quickly do almost anything that you please to datasets so large that the browser will have trouble with them before the library does. However, since D3 requires a fair amount of programming to get anything done, it doesn’t fit every project, and I often want something to fill that gap.

A hand with a pen touches up some charts

This week, for my Morning Dashboard project, I wanted a few straightforward visualizations of some data—I’ll talk about the details more in Monday’s developer diary post—and research led me to Chart.js.

I already knew about Chart.js, actually. You might recall my using it briefly for my post on redemption arcs and how that might apply to politics and other parts of life. I didn’t do much with it, though, and I haven’t had much need for data visualization on the blog, so I mostly forgot about it. But as I’ve looked at more data for the Morning Dashboard, I needed something quick.

The Basics

After including the Chart.js library on the page, we create a chart by providing a canvas element, wrapped in a div element. In my case, I set an explicit height on the wrapper, to ensure that the code doesn’t crush down the chart—or expand it up, in some cases—to where I have trouble reading it.

<div style="height: 300px">
  <canvas id="amounts">
  </canvas>
</div>

Then, on loading the page, create a Chart object on the canvas.

const ctx = document.getElementById('amounts');
new Chart(ctx, {
  type: "bar",
  data: {
    datasets: [
      {
        data: [
          30, 12, 4, 94, 0, 38, 65, 87,
          53, 46, 58, 416, 1097, 1312, 504, 237,
          713, 944, 1120, 668, 246, 107, 5, 3
        ],
        label: "Hourly Value"
        },
        {
          data: [
            53, 46, 58, 416, 1097, 1312, 504, 237,
            713, 944, 1120, 668, 246, 107, 5, 3,
            30, 12, 4, 94, 0, 38, 65, 87
          ],
          label: "Other Hourly Value"
      }
    ],
    labels: [
      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
      12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
    ]
  },
  options: {
    maintainAspectRatio: false,
    title: {
      display: false
    }
  }
});

That produces something that looks like this chart.

Nothing too difficult, there, as long as you get things right. The chart configuration requires a type—bar, line, etc.—one or more datasets, a shared set of labels per value in the datasets, and some options. In my case, the options allow me to set the height of the chart manually and not display a specific title to the result.

And I should mention that you get fairly nice results for this minimal effort. You get a friendly tool-tip when your mouse pointer runs over any of the bars, indicating the label (the horizontal axis), the name of the data, and the value. You can also interact with the legend, in that you can click on the different data sets to turn them on and off.

Enhancements

You can add a lot, past this point. Besides plenty of configuration options to change how the charts animate, handle layout, and so forth, each data set can take individual colors and weights.

For example, since I use this partly to visualize my television streaming, it makes sense for the chart to associate a given service with a dominant—or at least unique—color in its branding. And while bold blue and red have some overloaded meanings, enough services do have a distinct enough brand color that I can get away with this.

Pitfalls

It didn’t go perfectly, though. I spent a lot of time debugging what amounted to “it stubbornly won’t work without this.” As (probably) usual, I wrote the entire post as a vector to talk about the parts that struck me as obstacles, since I actually need to remember that part, and would like to help people avoid my mishaps.

No Legend, No Chart

In particular, I had to come to terms with the necessity for every data set to have a label of its own. Since I visualize this for myself and will see the data every day, the legend mostly clutters the valuable space. This struck me as highly counter-intuitive, since I don’t generally consider a legend part of its chart. However, it makes much more sense after discovering that the viewer can interact with the legend to change the view of the chart.

It made even more sense, after I discovered how to configure the legend, including turning it off, but that reminds me…

Legends: Part of the Chart, or Not?

When I say “discovered how to configure the legend,” I mean that unpleasantly literally.

For reasons that I don’t understand, the legends don’t configure intuitively. Reading that I could set a legend option, I assumed that .options.legend.relevantFeature would work. It doesn’t do anything, so I made the further assumption for a while that maybe legend configuration didn’t work.

It does work, but it appears that—much like myself—the Chart.js team doesn’t actually believe that Legends act as an important part of the chart. I missed a layer of indirection. In fact, to move the legend to the right-hand side of the images, I need to set .options.plugins.legend.location: right. They consider the legend a plugin, apparently…but you also can’t display an image, unless you populate all the information that the legend wants.

I don’t get it. But now I have it in a blog post, so I never have to question it again.

DIV-vy It Up

Whatever you do, try not to forget that div element—or presumably any element, but I didn’t feel like testing—wrapping the canvas element.

When creating this blog post, I neglected to copy the wrapper over from the source where I got it working. Instead of…well, actually, I don’t know if it rendered a graph. I do know that it tried to render something infinitely tall, though, which would have made it difficult to read the graph.

Aspect Ratios

Speaking of heights, you’ll apparently also generally want to set .options.maintainAspectRatio to false. If you don’t do that, the library imagines an optimal height and width for the chart and uses it, regardless of how cluttered or sparse the space becomes. By turning off the aspect ratio check, you can set the height and width of the container—the div element, described above—to whatever shape that you’d like the graph to fill.

Error-Free, Guaranteed

Several of the other issues raise a fifth nuisance in working through this: Unless you forget a bracket or otherwise foul up the JavaScript syntax, Chart.js will never complain about what you feed it. Instead, it’ll draw an empty graph, sometimes with a legend, but sometimes not, and you get to figure out what went wrong on your own time.

If I seem particularly irritated by the prior issues, I hope that this helps explain why. Rather than warning that .options.legend doesn’t exist, or that .data.datasets[0].label won’t render, the library fails silently.

Would I Use It Again?

Complaints aside—and I find it reasonable to push four out of the five aside, since they only require me to remember something that I have now published—Chart.js does a fairly nice job for fairly little work.

For an example of that return on effort invested, take a look at a preview for something that I’ll talk about in Monday’s post, as well as a small peek into where I watch things.

As with the bar charts, I can toggle the lines off that don’t interest me for certain questions. As a good example, since I didn’t bother to hide the service names (or did I…?), I could turn off the majority of services that don’t cost any money, so that I can see whether I should find a particular service worth the subscription fee. Or I could toggle off the paid subscriptions, to see if I can or should lean more heavily on them, to replace another paid service.

In other words, yes, I’ll continue to work with Chart.js. It probably won’t replace D3, but unlike D3, you can probably see how straightforward the code would look to extract a range of cells from a CSV file and turn them into a complete graph. A clever person could probably even write a single JavaScript function that does this automatically, but I’ll leave that as a figurative exercise, since I don’t need it.

Cheap Plug

By the way, if you find this post more interesting because it makes you wonder what I watch on Kanopy or the Roku Channel, the monthly Entropy Arbitrage newsletter—click Follow, in the upper-right of that page—has a section marked Media, where I talk about my opinions on what I’ve finished watching, listening to, or reading, and comes out every month. If you want to know those opinions immediately, then you might want to become a member, because I send members previews of what I write for the newsletter.


Credits: The header image is adapted from Hand business woman pointing paper data analyze chart on desk at office by asawin, made available under the terms of the Creative Commons CC0 1.0 Universal Public Domain Dedication.