{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 monthJan: 1,200; Feb: 950; Mar: 1,430; Apr: 1,61005001,0001,5002,000JanFebMarAprJan: 1,200Feb: 950Mar: 1,430Apr: 1,610

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

KindShowsOptions
linechange over timelabel, value
bar (or column), hbarcategories comparedlabel, value, stacked
pie, donutparts of a wholelabel, value
scatter, bubblehow two numbers relatex, y, size, label, color, trend
heatmappatterns in a grid of two categoriesx, y, value
sankeyflows from one stage to the nextsource, target, value
networkconnectionssource, target, value, layout, arrows
treemap, sunbursta hierarchy, each part's sharelabel, value, levels
sparklinea shape the size of a wordlabel, 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 monthJan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720OnlineShop05001,0001,5002,000JanFebMarAprJan · Online: 1,200Jan · Shop: 800Feb · Online: 950Feb · Shop: 1,100Mar · Online: 1,430Mar · Shop: 960Apr · Online: 1,610Apr · Shop: 720 Online and Shop by monthJan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720OnlineShop05001,0001,5002,0002,500JanFebMarAprJan · Online: 1,200Jan · Shop: 800Feb · Online: 950Feb · Shop: 1,100Mar · Online: 1,430Mar · Shop: 960Apr · Online: 1,610Apr · Shop: 720 Online and Shop by monthJan: Online 1,200, Shop 800; Feb: Online 950, Shop 1,100; Mar: Online 1,430, Shop 960; Apr: Online 1,610, Shop 720OnlineShop05001,0001,5002,000JanFebMarAprOnlineShopJan · Online: 1,200Feb · Online: 950Mar · Online: 1,430Apr · Online: 1,610Jan · Shop: 800Feb · Shop: 1,100Mar · Shop: 960Apr · 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 teamLions: 71; Tigers: 84; Bears: -12; Wolves: 55-20020406080100LionsTigersBearsWolvesLions: 71Tigers: 84Bears: -12Wolves: 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 postHousing: 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 postHousing: 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,150Housing 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 weightAlto: 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 22JapanEuropeUSA2025303540451,0001,2501,5001,7502,0002,250weightmpgJapan · Alto: weight 1,000, mpg 45Europe · Berlin: weight 1,200, mpg 40Japan · Corsa: weight 1,350, mpg 37USA · Dakota: weight 1,500, mpg 33USA · Eagle: weight 1,800, mpg 28USA · Falcon: weight 2,100, mpg 22Trend line Mpg against weightAlto: 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 3010203040505001,0001,5002,0002,500weightmpgFalcon: weight 2,100, mpg 22, sold 30Dakota: weight 1,500, mpg 33, sold 20Alto: weight 1,000, mpg 45, sold 12Eagle: weight 1,800, mpg 28, sold 8Berlin: weight 1,200, mpg 40, sold 5Corsa: 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 hourMon · 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: 4MonTueWed9h12h15hMon · 9h: 33Mon · 12h: 1212Mon · 15h: 77Tue · 9h: 55Tue · 12h: 2020Tue · 15h: 99Wed · 9h: 11Wed · 12h: 1515Wed · 15h: 44120

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 toCoal → Electricity: 40; Gas → Electricity: 30; Gas → Heat: 25; Solar → Electricity: 15; Electricity → Homes: 45; Electricity → Industry: 30; Electricity → Losses: 10; Heat → Homes: 25Coal → Electricity: 40Electricity → Homes: 45Electricity → Industry: 30Electricity → Losses: 10Gas → Electricity: 30Gas → Heat: 25Heat → Homes: 25Solar → Electricity: 15Coal: 40CoalElectricity: 85ElectricityGas: 55GasHeat: 25HeatSolar: 15SolarHomes: 70HomesIndustry: 30IndustryLosses: 10Losses

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 to6 nodes, 7 links; Ann → Bob; Ann → Cas; Bob → Cas; Cas → Dirk; Dirk → Eva; Eva → Fay; Fay → DirkAnn → BobAnn → CasBob → CasCas → DirkDirk → EvaEva → FayFay → DirkAnn: 2 linksAnnBob: 2 linksBobCas: 3 linksCasDirk: 3 linksDirkEva: 2 linksEvaFay: 2 linksFay Links between from and to6 nodes, 7 links; Ann → Bob; Ann → Cas; Bob → Cas; Cas → Dirk; Dirk → Eva; Eva → Fay; Fay → DirkAnn → BobAnn → CasBob → CasCas → DirkDirk → EvaEva → FayFay → DirkAnn: 2 linksAnnBob: 2 linksBobCas: 3 linksCasDirk: 3 linksDirkEva: 2 linksEvaFay: 2 linksFay

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 deptIT › Laptops: 500; IT › Cloud: 800; IT › Licences: 300; HR › Training: 400; HR › Recruiting: 250; Sales › Travel: 600; Sales › Events: 350IT › Laptops: 500Laptops500IT › Cloud: 800Cloud800IT › Licences: 300Licences300HR › Training: 400Training400HR › Recruiting: 250Recruiting250Sales › Travel: 600Travel600Sales › Events: 350Events350 Amount by deptIT › Laptops: 500; IT › Cloud: 800; IT › Licences: 300; HR › Training: 400; HR › Recruiting: 250; Sales › Travel: 600; Sales › Events: 350IT: 1,600HR: 650Sales: 950IT › Laptops: 500IT › Cloud: 800IT › Licences: 300HR › Training: 400HR › Recruiting: 250Sales › Travel: 600Sales › Events: 3503,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 terms1: 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.