responsive, retina-compatible charts with just an img tag
brought to you by

At StatHat, we make a lot of charts...

Our web app creates dynamic, autoupdating charts using JavaScript, but we also send charts via email and to external services like Campfire and Slack. Plus, we let users embed charts on their own web pages. For these situations, JavaScript charts either won't work, are overkill, or could get messy.

So we made chartd. It allows you to create a chart with just an img tag. These charts:

  • are responsive (resize this page to see the charts respond)
  • are retina-friendly
  • can contain up to 5 datasets
  • can be line or step charts
  • have customizable colors and strokes

No JavaScript is required. You can generate SVG or PNG charts. We prefer SVG, but some services (like gmail) won't render SVGs in emails.

<img src="">

Here's a basic chart. All charts must have these parameters: width (w), height (h), and a dataset (d0).

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst">

When you give chartd the ymin and ymax values, it will draw the y axes.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3">

Just show the left axis with ol=1 (or just the right with or=1).

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&ol=1">

You can add a title with the t parameter.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&ol=1&t=AAPL+(past+2+months)">

You can put up to 5 datasets (d0..d4) on each chart.

<img src="/a.svg?w=580&h=180&d0=RXZZfhgdURRUYZgfccZXUM&d1=roksqfdcjfKGGMQOSXchUO&d2=y3vuuvljghrgcYZZcdVckg&d3=kdfffcZYbggdfdhkkkgjgk&ymin=45&ymax=90&t=Mean Temperature">

Change any chart to a step chart by adding step=1.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&t=AAPL+(past+2+months)&step=1">

Highlight the last point with hl=1. This is the style StatHat uses for automatic anomaly detection emails.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&or=1&t=AAPL+(past+2+months)&hl=1">

Provide xmin and xmax as UNIX timestamps and chartd will render the times on the X axis.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&t=AAPL+(past+2+months)&xmin=1406260190&xmax=1411444204">

UTC is the default time zone. Change this with the tz parameter.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&t=AAPL+(past+2+months)&xmin=1406260190&xmax=1411444204&tz=America/Chicago">

You can change the stroke colors and fills.

<img src="/a.svg?w=580&h=180&d0=SRWfaZHLHEDABKKTUYgpqqvws0138eZfaYtwxxsxyst&ymin=94.48&ymax=103.3&t=AAPL+(past+2+months)&xmin=1406260190&xmax=1411444204&tz=America/Chicago&s0=FF0000&f0=00FF0080">

You can make the lines dashed or dotted.

<img src="/a.svg?w=580&h=180&d0=RXZZfhgdURRUYZgfccZXUM&d1=roksqfdcjfKGGMQOSXchUO&d2=y3vuuvljghrgcYZZcdVckg&ymin=45&ymax=90&s0=4991AE&s1=FF8300.&s2=FF5DAA-">

If you need a PNG chart instead of SVG, use a.png instead of a.svg.

<img src="/a.png?w=580&h=180&d0=RXZZfhgdURRUYZgfccZXUM&d1=roksqfdcjfKGGMQOSXchUO&d2=y3vuuvljghrgcYZZcdVckg&ymin=45&ymax=90&s0=4991AE&s1=FF8300.&s2=FF5DAA-">

Responsive SVG

The SVG chart images are vectors and can be scaled down for smaller screens. If you're on a desktop browser, go ahead and resize this window to see the charts scale.

To make this happen, wrap the image in an svg-container class and give the image tag an svg-content class.

Here's the CSS for svg-container and svg-content:

.svg-container {
        display: inline-block;
        position: relative;
        vertical-align: middle;
        overflow: hidden;
        width: 100%;
        padding-bottom: 32%;

.svg-content {
        display: inline-block;
        position: absolute;
        top: 0;
        left: 0;

The only tricky thing is that you need the padding-bottom percentage to be at least as great as the ratio of the chart height to its width. The charts on this page are 580px wide by 180px high. So the ratio of height to width is just over 31%, so we rounded up to 32%. You can make it larger to add more padding below the charts.

<div class="svg-container">
    <img src="/a.svg?w=580&h=180&d0=ABCDEFGHIJKLMNOPZaz09&ymin=0&ymax=100" class="svg-content">

Responsive PNG

While they don't scale as nicely as SVGs, you can make the PNG charts responsive as well. Add the img-responsive class to the PNG img tag and make sure the width you provide as a parameter matches the width of the container.

Here's the CSS for img-responsive (borrowed from Bootstrap 3):

.img-responsive {
  display: block;
  width: 100% \9;
  max-width: 100%;
  height: auto;
<img src="/a.png?w=580&h=180&d0=ABCDEFGHIJKLMNOPZaz09&ymin=0&ymax=100" class="img-responsive">

Don't care about responsive?

Then just provide the width and height to the img tag for SVG or PNG. It won't resize when displayed on a smaller screen (but will still look great on retina displays).

<img src="" width="580" height="180">

Speaking of retina...

All chartd charts look great on retina displays. The SVG charts are vectors and render perfectly. chartd generates all the PNGs @2x the resolution so they scale appropriately.

All parameters, explained.

There are only two URLs: /a.svg and /a.png. Use /a.svg for SVG charts and /a.png for PNG charts.

Here are all the possible chartd parameters:

parameterdescriptiondata typerequired?
wchart widthintegeryes
hchart heightintegeryes
d0dataset 0encoded stringyes
d1dataset 1encoded stringno
d2dataset 2encoded stringno
d3dataset 3encoded stringno
d4dataset 4encoded stringno
s0color, stroke for d0stroke stringno
s1color, stroke for d1stroke stringno
s2color, stroke for d2stroke stringno
s3color, stroke for d3stroke stringno
s4color, stroke for d4stroke stringno
f0fill color for d0stroke stringno
f1fill color for d1stroke stringno
f2fill color for d2stroke stringno
f3fill color for d3stroke stringno
f4fill color for d4stroke stringno
yminy minimumfloatno
ymaxy maximumfloatno
xminx minimumUNIX timestampno
xmaxx maximumUNIX timestampno
tztimezone for X axis datesIANA Time Zone location name (e.g. America/New_York or Europe/Berlin)no
tchart titlestringno
stepstep chart1 to turn onno
hlhighlight last point1 to turn onno
olonly left y-axis1 to turn onno
oronly right y-axis1 to turn onno

Data encoding

The data is encoded into a base-62 string. Each character in the string is a data point. Each character represents a value of 0 to 61. The characters are A..Za..z0..9. 'A' is 0, '9' is 61. To encode, you map the Y values in your data onto the base-62 characters. Here's what it looks like in Go:

const b62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

func encode(data []float64, min, max float64) string {
        r := math.Dim(max, min)
        bs := make([]byte, len(data))
        if r == 0 {
                for i := 0; i < len(data); i++ {
                        bs[i] = b62[0]
                return string(bs)
        enclen := float64(len(b62) - 1)
        for i, y := range data {
                index := int(enclen * (y - min) / r)
                if index >= 0 && index < len(b62) {
                        bs[i] = b62[index]
                } else {
                        bs[i] = b62[0]
        return string(bs)

Stroke string

You can customize the color and the stroke of each dataset with the s0 - s4 parameters and the fill color with the f0 - f4 parameters. Each stroke string is 6-9 characters long. The first 6 characters are your typical CSS hexadecimal RGB values. The next two characters are an optional hexadecimal alpha value. The final character is on optional stroke parameter that determines if the line is solid, dashed, or dotted. The stroke parameter is ignored for fill parameters.

Here's how it looks:


The RR, GG, BB, and AA fields can go from 00 to FF.

The S field is either - for dashed or . for dotted (anything else, including nothing, will be a solid line).

Some examples:

4991AE blue solid line, no alpha

EFF3F680 red with 50% alpha.

4991AE- blue dashed line, no alpha

FF8300. orange dotted line, no alpha

FF830080. orange dotted line, 50% alpha

SSL, etc.

All chartd charts are also available over SSL. Just use

chartd makes nice huge charts.

and tiny charts too: