Charts

{chart} draws a chart as inline SVG on the server. There is no JavaScript, so the chart prints, works where scripts do not run, and caches like any other output. A bar chart puts columns on a zero baseline; a line chart draws a line over a light wash with the last point marked.

PHP
fragments/charts_1.php
HTML
fragments/charts_1.pad
Result
<?php

$sales
= [
[
'month' => 'Jan', 'amount' => 1200 ],
[
'month' => 'Feb', 'amount' => 950 ],
[
'month' => 'Mar', 'amount' => 1430 ],
[
'month' => 'Apr', 'amount' => 1610 ]
];

?>
{chart 'bar', data='sales', label='month', value='amount',
title='Sales per month', width=480, height=200
}
Sales per month Jan: 1,200; Feb: 950; Mar: 1,430; Apr: 1,610 0 500 1,000 1,500 2,000 Jan Feb Mar Apr Jan: 1,200 Feb: 950 Mar: 1,430 Apr: 1,610

The first word picks the kind of chart, after the job the data has to do:

Kind Shows Options
line change over time label, value
bar (or column), hbar categories compared label, value, stacked
pie, donut parts of a whole label, value
scatter, bubble how two numbers relate x, y, size, label, color, trend
heatmap patterns in a grid of two categories x, y, value
sankey flows from one stage to the next source, target, value
network connections source, target, value, layout, arrows
treemap, sunburst a hierarchy, each part's share label, value, levels
sparkline a shape the size of a word label, value

An option left out takes the first field that fits: a numeric one for a number, another one for a name.

Where the numbers come from

Several series

A list of fields in value= draws a series each - bars side by side, or on top of each other with stacked, or a line each, named at its end. A legend above the plot names them in the fixed order of the colours; eight series at most.

PHP
fragments/charts_3.php
HTML
fragments/charts_3.pad
Result
<?php

$sales
= [
[
'month' => 'Jan', 'online' => 1200, 'shop' => 800 ],
[
'month' => 'Feb', 'online' => 950, 'shop' => 1100 ],
[
'month' => 'Mar', 'online' => 1430, 'shop' => 960 ],
[
'month' => 'Apr', 'online' => 1610, 'shop' => 720 ]
];

?>
{chart 'bar', data='sales', label='month', value='online, shop', width=480, height=220}
{chart 'bar', data='sales', label='month', value='online, shop', stacked, width=480, height=220}
{chart 'line', data='sales', label='month', value='online, shop', width=480, height=220}
Online and Shop by month Jan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720 Online Shop 0 500 1,000 1,500 2,000 Jan Feb Mar Apr Jan · Online: 1,200 Jan · Shop: 800 Feb · Online: 950 Feb · Shop: 1,100 Mar · Online: 1,430 Mar · Shop: 960 Apr · Online: 1,610 Apr · Shop: 720 Online and Shop by month Jan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720 Online Shop 0 500 1,000 1,500 2,000 2,500 Jan Feb Mar Apr Jan · Online: 1,200 Jan · Shop: 800 Feb · Online: 950 Feb · Shop: 1,100 Mar · Online: 1,430 Mar · Shop: 960 Apr · Online: 1,610 Apr · Shop: 720 Online and Shop by month Jan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720 Online Shop 0 500 1,000 1,500 2,000 Jan Feb Mar Apr Online Shop Jan · Online: 1,200 Feb · Online: 950 Mar · Online: 1,430 Apr · Online: 1,610 Jan · Shop: 800 Feb · Shop: 1,100 Mar · Shop: 960 Apr · Shop: 720

Horizontal bars

hbar lays the bars down, the names on the left - for long names or many categories.

PHP
HTML
fragments/charts_4.pad
Result
{data 'teams'}
team,points
Lions,71
Tigers,84
Bears,-12
Wolves,55
{/data}
{chart 'hbar', data='teams', label='team', value='points', width=480, height=200}
Points by team Lions: 71; Tigers: 84; Bears: -12; Wolves: 55 -20 0 20 40 60 80 100 Lions Tigers Bears Wolves Lions: 71 Tigers: 84 Bears: -12 Wolves: 55

Pie and donut

Parts of a whole: only positive values count, clockwise from twelve o'clock, a legend with each value and its share. Past eight parts the smallest are folded into one grey Other. A donut shows the total in its middle.

PHP
HTML
fragments/charts_5.pad
Result
{data 'budget'}
post,amount
Housing,1200
Food,450
Transport,200
Savings,300
{/data}
{chart 'pie', data='budget', label='post', value='amount', width=420, height=200}
{chart 'donut', data='budget', label='post', value='amount', width=420, height=200}
Amount by post Housing: 1,200 (55.8%); Food: 450 (20.9%); Transport: 200 (9.3%); Savings: 300 (14%) Housing: 1,200 (55.8%) Food: 450 (20.9%) Transport: 200 (9.3%) Savings: 300 (14%) Housing 1,200 (55.8%) Food 450 (20.9%) Transport 200 (9.3%) Savings 300 (14%) Amount by post Housing: 1,200 (55.8%); Food: 450 (20.9%); Transport: 200 (9.3%); Savings: 300 (14%) Housing: 1,200 (55.8%) Food: 450 (20.9%) Transport: 200 (9.3%) Savings: 300 (14%) 2,150 Housing 1,200 (55.8%) Food 450 (20.9%) Transport 200 (9.3%) Savings 300 (14%)

Scatter and bubble

A dot per row on two numeric axes, x= and y=. color= colours the dots by a field, trend draws the least-squares line. A bubble's size= sets its area.

PHP
fragments/charts_6.php
HTML
fragments/charts_6.pad
Result
<?php

$cars
= [
[
'model' => 'Alto', 'weight' => 1000, 'mpg' => 45, 'origin' => 'Japan', 'sold' => 12 ],
[
'model' => 'Berlin', 'weight' => 1200, 'mpg' => 40, 'origin' => 'Europe', 'sold' => 5 ],
[
'model' => 'Corsa', 'weight' => 1350, 'mpg' => 37, 'origin' => 'Japan', 'sold' => 3 ],
[
'model' => 'Dakota', 'weight' => 1500, 'mpg' => 33, 'origin' => 'USA', 'sold' => 20 ],
[
'model' => 'Eagle', 'weight' => 1800, 'mpg' => 28, 'origin' => 'USA', 'sold' => 8 ],
[
'model' => 'Falcon', 'weight' => 2100, 'mpg' => 22, 'origin' => 'USA', 'sold' => 30 ]
];

?>
{chart 'scatter', data='cars', x='weight', y='mpg', label='model', color='origin', trend, width=480, height=240}
{chart 'bubble', data='cars', x='weight', y='mpg', size='sold', label='model', width=480, height=240}
Mpg against weight Alto: weight 1,000, mpg 45; Berlin: weight 1,200, mpg 40; Corsa: weight 1,350, mpg 37; Dakota: weight 1,500, mpg 33; Eagle: weight 1,800, mpg 28; Falcon: weight 2,100, mpg 22 Japan Europe USA 20 25 30 35 40 45 1,000 1,250 1,500 1,750 2,000 2,250 weight mpg Japan · Alto: weight 1,000, mpg 45 Europe · Berlin: weight 1,200, mpg 40 Japan · Corsa: weight 1,350, mpg 37 USA · Dakota: weight 1,500, mpg 33 USA · Eagle: weight 1,800, mpg 28 USA · Falcon: weight 2,100, mpg 22 Trend line Mpg against weight Alto: weight 1,000, mpg 45, sold 12; Berlin: weight 1,200, mpg 40, sold 5; Corsa: weight 1,350, mpg 37, sold 3; Dakota: weight 1,500, mpg 33, sold 20; Eagle: weight 1,800, mpg 28, sold 8; Falcon: weight 2,100, mpg 22, sold 30 10 20 30 40 50 500 1,000 1,500 2,000 2,500 weight mpg Falcon: weight 2,100, mpg 22, sold 30 Dakota: weight 1,500, mpg 33, sold 20 Alto: weight 1,000, mpg 45, sold 12 Eagle: weight 1,800, mpg 28, sold 8 Berlin: weight 1,200, mpg 40, sold 5 Corsa: weight 1,350, mpg 37, sold 3

Heatmap

Two categories as columns (x=) and rows (y=), each cell coloured by its value in seven steps of one hue, with the scale underneath.

PHP
HTML
fragments/charts_7.pad
Result
{data 'visits'}
day,hour,count
Mon,9h,3
Mon,12h,12
Mon,15h,7
Tue,9h,5
Tue,12h,20
Tue,15h,9
Wed,9h,1
Wed,12h,15
Wed,15h,4
{/data}
{chart 'heatmap', data='visits', x='hour', y='day', value='count', width=420, height=200}
Count by day and hour Mon · 9h: 3; Mon · 12h: 12; Mon · 15h: 7; Tue · 9h: 5; Tue · 12h: 20; Tue · 15h: 9; Wed · 9h: 1; Wed · 12h: 15; Wed · 15h: 4 Mon Tue Wed 9h 12h 15h Mon · 9h: 3 3 Mon · 12h: 12 12 Mon · 15h: 7 7 Tue · 9h: 5 5 Tue · 12h: 20 20 Tue · 15h: 9 9 Wed · 9h: 1 1 Wed · 12h: 15 15 Wed · 15h: 4 4 1 20

Sankey

A row is a flow from source= to target=, as wide as its value=. The columns follow the flows; flows that go round in a circle are an error.

PHP
HTML
fragments/charts_8.pad
Result
{data 'energy'}
from,to,amount
Coal,Electricity,40
Gas,Electricity,30
Gas,Heat,25
Solar,Electricity,15
Electricity,Homes,45
Electricity,Industry,30
Electricity,Losses,10
Heat,Homes,25
{/data}
{chart 'sankey', data='energy', source='from', target='to', value='amount', width=480, height=260}
Flows from from to to Coal → Electricity: 40; Gas → Electricity: 30; Gas → Heat: 25; Solar → Electricity: 15; Electricity → Homes: 45; Electricity → Industry: 30; Electricity → Losses: 10; Heat → Homes: 25 Coal → Electricity: 40 Electricity → Homes: 45 Electricity → Industry: 30 Electricity → Losses: 10 Gas → Electricity: 30 Gas → Heat: 25 Heat → Homes: 25 Solar → Electricity: 15 Coal: 40 Coal Electricity: 85 Electricity Gas: 55 Gas Heat: 25 Heat Solar: 15 Solar Homes: 70 Homes Industry: 30 Industry Losses: 10 Losses

Network

A row is a link between two nodes. The default layout='force' pulls linked nodes together - the same data always draws the same picture - and layout='radial' sets them on a circle; arrows shows the direction.

PHP
HTML
fragments/charts_9.pad
Result
{data 'friends'}
from,to
Ann,Bob
Ann,Cas
Bob,Cas
Cas,Dirk
Dirk,Eva
Eva,Fay
Fay,Dirk
{/data}
{chart 'network', data='friends', source='from', target='to', arrows, width=420, height=260}
{chart 'network', data='friends', source='from', target='to', layout='radial', width=420, height=260}
Links between from and to 6 nodes, 7 links; Ann → Bob; Ann → Cas; Bob → Cas; Cas → Dirk; Dirk → Eva; Eva → Fay; Fay → Dirk Ann → Bob Ann → Cas Bob → Cas Cas → Dirk Dirk → Eva Eva → Fay Fay → Dirk Ann: 2 links Ann Bob: 2 links Bob Cas: 3 links Cas Dirk: 3 links Dirk Eva: 2 links Eva Fay: 2 links Fay Links between from and to 6 nodes, 7 links; Ann → Bob; Ann → Cas; Bob → Cas; Cas → Dirk; Dirk → Eva; Eva → Fay; Fay → Dirk Ann → Bob Ann → Cas Bob → Cas Cas → Dirk Dirk → Eva Eva → Fay Fay → Dirk Ann: 2 links Ann Bob: 2 links Bob Cas: 3 links Cas Dirk: 3 links Dirk Eva: 2 links Eva Fay: 2 links Fay

Treemap and sunburst

levels= lists the fields above the parts, the outermost first. A treemap nests rectangles, a sunburst rings; the colour is that of the outermost level.

PHP
HTML
fragments/charts_10.pad
Result
{data 'spend'}
dept,post,amount
IT,Laptops,500
IT,Cloud,800
IT,Licences,300
HR,Training,400
HR,Recruiting,250
Sales,Travel,600
Sales,Events,350
{/data}
{chart 'treemap', data='spend', levels='dept', label='post', value='amount', width=480, height=260}
{chart 'sunburst', data='spend', levels='dept', label='post', value='amount', width=300, height=300}
Amount by dept IT › Laptops: 500; IT › Cloud: 800; IT › Licences: 300; HR › Training: 400; HR › Recruiting: 250; Sales › Travel: 600; Sales › Events: 350 IT › Laptops: 500 Laptops 500 IT › Cloud: 800 Cloud 800 IT › Licences: 300 Licences 300 HR › Training: 400 Training 400 HR › Recruiting: 250 Recruiting 250 Sales › Travel: 600 Travel 600 Sales › Events: 350 Events 350 Amount by dept IT › Laptops: 500; IT › Cloud: 800; IT › Licences: 300; HR › Training: 400; HR › Recruiting: 250; Sales › Travel: 600; Sales › Events: 350 IT: 1,600 HR: 650 Sales: 950 IT › Laptops: 500 IT › Cloud: 800 IT › Licences: 300 HR › Training: 400 HR › Recruiting: 250 Sales › Travel: 600 Sales › Events: 350 3,200

Sparklines

{sparkline} is a line chart the size of a word, without axes, to stand in a sentence or a table cell.

PHP
HTML
fragments/charts_2.pad
Result
<p>Fibonacci grows fast: {sparkline sequence='fibonacci', rows=12}</p>

Fibonacci grows fast: Fibonacci, the first 12 terms 1: 0; 2: 1; 3: 1; 4: 2; 5: 3; 6: 5; 7: 8; 8: 13; 9: 21; 10: 34; 11: 55; 12: 89

Accessible and themeable

Every chart is an image to a screen reader (role="img"), named by its title= and described by a list of its values. Every bar, point, slice, cell, flow and node carries its own tooltip; a sparkline has none. The colours are CSS custom properties - --pad-chart-series, --pad-chart-text, --pad-chart-grid, --pad-chart-surface, the categorical --pad-chart-1 to --pad-chart-8, --pad-chart-other and the heatmap's --pad-chart-heat-0 to --pad-chart-heat-6 - set on .pad-chart, and their defaults follow the page's color-scheme, light or dark.