Sparklines
posted by Stephan Brumme
Why sparklines ?
When I started my realtime logfile analysis website status.stephan-brumme.com, I was looking for a way to display activities, such as traffic or hits, in compact yet intuitive way.A primary concern was that standard diagrams just take too much space. I gradually reduced their size until they had about the same height as the surrounding text:
214x83 pixels: 107x42 pixels: 54x21 pixels: 27x11 pixels:
It's impossible to have any readable legend in the smallest diagram - and I don't really need it. All I want to know is: is a lot going on ? or is my server pretty much dead ? The small diagram fits these needs.
I wasn't the first to come up with the idea of diagrams scaled down to the size of text. Edward Tufte published some of the most impressive books about information visualization and coined the term sparklines.
There is a huge variaties of sparklines, not only the classic line chart. I wanted something more computer-like and ended up with this drawing style: .
Live Demo
Sparklines at status.stephan-brumme.com
There are very powerful PHP scripts available on the internet but I wanted something simple in under 100 lines.My sparklines consist of 24 bars, each representing 1 hour: the darker the bar, the more traffic or hits. On Tufte's website he varies the bars' width instead of their colors. Probably his approach is better suited for black/white printing.
The sparkline doesn't know anything about the data it displays. It gets only a bunch of numbers and creates an image.
Hence I needed an efficient URL encoding scheme to forward these numbers to
sparkline.php
:
each hour is represented by a single digit, a total of 24 digits (= 24 hours).
We are left with only 10 possible values per hour but it's absolutely fine with my needs.In the end, the url
sparkline.php?123456789159843695782554
produces
.If you need more precision then consider hexadecimal digits (10+6=16) or include the whole alphabet (10+26=36), maybe distinguish upper and lower case letters (10+26+26=62). There is a endless number of encoding schemes.
A good bar size ratio height/width seems to be about 3. My default height is 10 therefore my default width became 3.
These values can be adjusted to your needs: look out for
$height
and $width
.During my tests I found out that it's much better to leave some space between bars, I chose 1 pixel. To keep the code flexible, just can modify the width of the space by modifying
$padding
.Actually my script doesn't care about the amount of digits in its URL. Everything is still working fine if you encode only 10 values instead of 24 in its URL.
My PHP script produces PNG images only. For GIFs you need to remove the transparency code. Do not replace PNG by JPEG because they tend to have a bad compression ratio for such small files and often carry visible rendering artifacts. My PNG are typically 100 to 200 bytes small.
Remember: my default settings are not rocket science but only some numbers I found useful during my tests.
Download
Latest release: April 24, 2013, size: 2400 bytes, 81 linesCRC32:
e772e2b1
MD5:
7d9a3c100ecaecce5d519918c25b8c54
SHA1:
33f2942fa5294c05678965478331e15c499657a3
SHA256:
4d1d1a89e6bce537e8e9e35bfd8fc26125ffbe339d05da9ee27950e0628e1529
If you encounter any bugs/problems or have ideas for improving future versions, please write me an email: create@stephan-brumme.com
Source Code
hide
sparkline.php
<?php
// //////////////////////////////////////////////////////////
// sparkline.php
// Copyright (c) 2011 Stephan Brumme. All rights reserved.
// see https://create.stephan-brumme.com/disclaimer.html
//
// process parameters
$query = $_SERVER["QUERY_STRING"];
if (empty($query))
die;
// only numbers
$numValues = strlen($query);
for ($i = 0; $i < $numValues; $i++)
$hours[$i] = intval($query[$i]);
// upper limit
$maxValue = max($hours);
if ($maxValue == 0)
$maxValue = 1;
// bars' configuration
$barWidth = 3;
$barHeight = 10;
$padding = 1;
$minGray = 0.3;
$maxGray = 1.0;
$width = $numValues * ($barWidth+$padding) - 1;
$height = $barHeight;
// add borders
$width += 2;
$height += 2;
// create new image, full color range
$img = imagecreatetruecolor($width, $height);
imageantialias ($img, true);
imagealphablending($img, false);
imagesavealpha ($img, true);
// transparent background
$background = imagecolorallocatealpha($img, 0,0,0, 127);
imagefilledrectangle ($img, 1,1, $width-2,$height-2, $background);
imagecolortransparent($img, $background);
// black border
$black = imagecolorallocatealpha($img, 0,0,0, 0);
imagerectangle($img, 0,0, $width-1,$height-1, $black);
// sparklines
$x = 1;
foreach ($hours as $hour)
{
$intensity = $hour/$maxValue;
// scale to prevent too bright/dark colors
if ($intensity > 0)
$intensity = $minGray + ($maxGray-$minGray)*$intensity;
// the higher the number, the darker (not brighter)
$intensity = 1 - $intensity;
// convert to grayscale (red == green == blue)
$gray = imagecolorallocatealpha($img, 0xFF*$intensity, // red
0xFF*$intensity, // green
0xFF*$intensity, // blue
0x7F*$intensity); // alpha
// draw single bar
imagefilledrectangle($img, $x,1, $x+$barWidth-1,$barHeight, $gray);
// next one
$x += $barWidth + $padding;
}
// send to client, use some caching
$secondsCache = 3600*24*7+1;
header("Content-type: image/png");
header("Last-Modified: ".gmdate("D, d M Y H:i:s", time())." GMT");
header("Cache-Control: max-age=$secondsCache");
header("Expires: ".gmdate("D, d M Y H:i:s", time()+$secondsCache)." GMT");
imagepng($img);
?>
Disclaimer
I'm no lawyer but recently became aware that Microsoft holds some patents on sparklines.To me there are tons of prior art on this topic rendering the patents invalid - but again: I'm no lawyer.