How and why I made EpochCharts.js

  • This post has all the technical details. If you just want to see what EpochCharts does, and how you can use it to learn from your data, read the announcement post.

Why make EpochChart?

One of the teams I work with wanted to see how their product’s growth numbers compared to the timing of real-world milestones.

It’s easy to graph those numbers on their own, but marking up those charts with any extra data was something no existing libraries did well.

To get the type of visual we wanted, we needed something custom. Thus EpochChart was born.

How was it made?

EpochCharts is a jQuery plugin for graphing line data, overlaid with arbitrary events. Use it to add real-world context to your numeric data, by tagging charts with the timing of real-world events.

Existing libraries we tried before making EpochCharts:

This isn’t something most charting packages can do out of the box. For example:

  • Flot has annotations that can be added to a chart, but the text is written onto the face of the data, which will get crowded with a number of annotations. 
  • Chart.js has a very nice API for rendering data, but no way to mark up the line with named events.
  • D3.js would probably work, if you wanted to build everything from scratch.
  • Highcharts (which EpochCharts is built on top of) doesn’t have out of the box support for annotating a line chart with extra events, so we hacked that on.

Adding Event Markers to Highcharts:

Highcharts does not support a line graph, with arbitrary or intermittent markers, out of the box, but it is very power and flexible.

To get the functionality we wanted, we start with a basic line:

[code language=”javascript”]

$(this).highcharts({
series: […]
})

[/code]

Removed its markers, using:


[code language="javascript"]
$(this).highcharts({
plotOptions: {
spline: {
marker: {enabled: false }
}
},
series: [{
type: 'spline',
data: [...]
}]
});
[/code]

Then, added markers for each event back on, by overlaying our line data with a custom-built scatter plot:

(That's right, the markers aren't actually on the lines, they're just scatter points placed at the right x,y coordinates)

[code language="javascript"]
$(this).highcharts({
plotOptions: {
spline: {
marker: {enabled: false }
}
},
series: [{
type: 'spline',
data: [...]
},
{
type: 'scatter',
data: markerData
}]
});
[/code]

Making it look right took a little love:

Putting custom markers in the right places

Since the events we want to tag are independent of the data points making the line, we need to calculate the effective Y value at which to render the marker so that it attaches to the line.

[code language="javascript"]
tallestPoint = function(lines, x) {
var computedValues, ret;
computedValues = $.map(lines, function(line) {
var prevPoint, ret;
prevPoint = null;
ret = null;
$.map(line['data'], function(datum, i) {
var slope, xdiff;
if (x >= datum['x']) {
prevPoint = datum;
return;
}
if (x < datum['x']) {
if (prevPoint === null) {
ret = datum['y'];
return;
}
xdiff = x - prevPoint['x'];
slope = (datum['y'] - prevPoint['y']) / (datum['x'] - prevPoint['x']);
ret = prevPoint['y'] + slope * (x - prevPoint['x']);
}
});
return ret;
});
ret = null;
$.each(computedValues, function(i, v) {
if ((v != null) && (!(ret != null) || ret < v)) {
return ret = v;
}
});
return ret;
};
[/code]

And we had to make the image for the marker twice as tall as the marker actually appears, because Highcharts tries to "center" the marker over the X/Y coordinate, and we wanted something that looked like a pin attached to the line.

Taming Highchart's default tooltips

By default, Highcharts uses some smart behaviors that, in most situations, make the tooltips very useful and intuitive. Applied to what we're doing with Highcharts, however, those behaviors tend to get in the way more than help.

We changed tooltips by:

  • Making them fixed positioned in the top left by default, and exposed an option `tooltip: {x:..., y:...}` to set your own custom placement.
  • Customizing the contents, using Highchart's native `Tooltip.formatted` option. The new format puts the date in the header, and looks for either the series name and Y-value of a data point, or the content of the custom marker as the body.
  • Disabling shared tooltips, which stops the whole chart body from acting as a hotspot, so tooltips only show when you're hovering closely over a specific point.

The result is now tooltips that appear when you expect, and display a tidy message in the upper left as needed.

Packing it up:

The plugin is written in coffeescript, which normally wants to add a layer of sandboxing to each file. To break out of it, I used  this simple trick from jashkenas on Stackoverflow.

That's it!

To use EpochChart, pull the code from github.

If you use it, let me know what you think. I'm on Twitter: @jfeldstein

Jordan is a freelance engineer with full-stack chops, and an eye for analytics and growth.