Dataplane · Visuals Lab

Website visuals, from first principles

A hundred and five hand-built, dependency-free web visuals — no chart library, no WebGL engine, no images. Each is a single file, runs on the GPU compositor, respects prefers-reduced-motion, and gzips to ≤2.3 KB. Everything below the foundations is wired to real Dataplane case studies — because on a data consultancy's site, a visual should carry an argument, not just decorate. Every card's ✎ Edit opens it in the visual editor: retune type, colour and line weight, drag the inflection points, then download the edited copy. The live data layer near the bottom renders the same idioms from real CSV/JSON.

Open the visual editor Which visual? — the advisor

Foundations — atmosphere & reusable primitives

The building blocks: a hero background, the pipeline motif, a KPI ring, and a drop-in stats bar. Learn these four recipes and the rest are variations.

01

Aurora mesh gradient

The Stripe hero look without the WebGL — blurred brand-hue blobs drifting out of phase, finished with procedural film grain.

radial-gradientCSS blur+driftfeTurbulence
No JS · no images1.9 KB gzip
03

Data pipeline flow

Paper-plane packets (the logo glyph, reused) stream Sources → Ingest → Model → Insight. The brand motif, in motion.

animateMotion+mpathrotate=auto
No JS · no images1.8 KB gzip
04

Animated KPI ring

A conic gradient you can actually animate, via the @property trick, with a count-up in the hole. One hard number, well told.

@propertyconic-gradientcount-up
12 lines JS1.6 KB gzip
14

Stat bar count-up

Your existing <StatsBar> (17 / 25 / 43 / 7) with figures that count up and accent rules that grow in. Drop-in.

Maps to: the live site's StatsBar
count-upscaleX
15 lines JS1.5 KB gzip

Insight visuals — mapped to real Dataplane work

Every one of these encodes an actual case-study claim. Numbers are illustrative, but the shape of the argument is the real work.

06

Store-opening causal lift

Treatment and control run parallel until the store opens, then treatment lifts away from its own counterfactual — the causal effect a naive before/after would miss.

Maps to: “What does opening a store do to online sales?”
stroke-dashoffseteffect polygon
No JS1.9 KB gzip
08

Every system agrees

Settlements tie out to the ledger; matches pass silently and dim, the one mismatch stays lit as a same-day exception with its records attached.

Maps to: “Getting every system to agree about every sale”
connector revealexception pulse
No JS2.0 KB gzip
05

Where the gap was hiding

A recovered reconciliation gap, broken to root cause in four bars that grow on the GPU with rand values counting up in sync.

Maps to: reconciliation root-cause analysis
scaleX staggercount-up
15 lines JS1.8 KB gzip
07

Who wins, who gives back

A candidate commission scheme replayed on real transactions — each rep's pay slope named as a gain (teal) or a give-back (amber) before anyone's pay changes.

Maps to: “Redesigning a sales commission scheme”
slope chartstroke-dashoffset
No JS1.8 KB gzip
09

Audit, then migrate

The BI estate audited by real usage: dead datasets shed in place, the rest carried across the Redshift → Snowflake divide, each landing with a verified-parity tick.

Maps to: “Migrating a retail BI estate to Snowflake”
grid transformsstagger
No JS1.8 KB gzip
10

Feed health, caught today

A daily scan sweeps the catalogue; a product flips to disapproved and an alert fires the same day — not at month-end after weeks of wasted spend.

Maps to: “Catching product-feed problems before they cost a month”
status gridscan sweeptoast
No JS1.8 KB gzip
11

System proposes, human disposes

A change proposal travels the pipeline and pauses at the approval gate until a human ✓ stamps it — then applies, with a loop back to auto-re-check in two weeks.

Maps to: “An ad-management platform with a human in the loop”
animateMotion holdkeyTimes
No JS2.1 KB gzip
13

One definition → every brand

A version-controlled dashboard template stamps itself out into per-merchant dashboards, each in its own hue — asset bundles re-deployed by script, not rebuilt by hand.

Maps to: “Merchant analytics for a multi-brand loyalty platform”
template pop-inper-brand var
No JS1.9 KB gzip
15

Retention by cohort

A retention heatmap washing in on a diagonal, each cell's colour driven by one custom property — the cohort view behind the store-impact and edtech work.

Maps to: cohort analysis (store-impact · edtech)
alpha heatmapdiagonal reveal
No JS1.6 KB gzip
02

Self-drawing insight line

A revenue line that draws itself in, area fill wiping behind it, a dot riding the exact curve — the headline lands only once the line arrives.

Maps to: “after the fix, revenue did this” moments
stroke-dashoffsetclip-path wipeanimateMotion
No JS2.1 KB gzip
12

Prospect radar

A rotating beam sweeps and prospects light up as blips as it passes, qualified ones held brighter in teal — the growth engine, made visible.

Maps to: the UK/US Shopify prospect radar
conic sweepstaggered blips
No JS1.5 KB gzip

Comparison & change — who moved, against what

Before/after, gaps, ranks and profiles. The common trick: keep every entity's own line or row, so nothing can hide inside an average.

18

Slopegraph, before → after

Cost per workload across the migration — every workload keeps its own line, and the one that got more expensive stays amber, on the record.

Maps to: “Migrating a retail BI estate to Snowflake”
line drawgutter labels
No JS1.8 KB gzip
22

Dumbbell gap

Straight-line vs road distance per suburb — the connector makes the gap the hero, and the mountain-detour row goes amber. Why 17% of customers flip nearest-store.

Maps to: geo / catchment routing work
connector growdot pop
No JS1.9 KB gzip
32

Diverging bars

Per-store lift and give-back from a shared zero — gains teal, losses amber, sorted so the ranking is free. Everyone named before the decision.

Maps to: store-impact · commission replay
scaleX from zerodual origin
No JS1.6 KB gzip
21

Bump chart

Category ranks over four quarters — the “who overtook whom” story a revenue line hides. Only the climber gets the accent.

Maps to: merchandising / category reviews
rank lanesline draw
No JS1.6 KB gzip
19

Bullet KPIs

The grown-up gauge: measure vs target vs last year in one slim row, with qualitative bands behind. Three of these replace a wall of dials.

Maps to: exec dashboards (dashboards-as-code)
layered trackstarget tick
No JS1.7 KB gzip
43

Radar, month 0 vs 6

Data-maturity profile at engagement start and end — same five axes, same scale, and the gap between the polygons is the engagement.

Maps to: maturity assessments / retainer reviews
polygon drawpentagon rings
No JS1.7 KB gzip

Composition & flow — what the whole is made of

Parts of a whole, and how volume moves between states. Area and width are the honest encodings here; the waffle keeps it human.

17

Waterfall bridge

Opening cash to closing cash with every step named — inflows teal, outflows amber, dashed connectors carrying the running level.

Maps to: cashflow memos / monthly close
scaleY staggerlevel connectors
No JS1.8 KB gzip
16

Funnel with the leak named

Session → paid, but the finding is the DROP between stages: the worst step lights amber with its diagnosis, not just its percentage.

Maps to: ecommerce conversion diagnostics
center scaleXdrop callouts
No JS1.8 KB gzip
28

Attribution flow

Channels → outcomes with ribbon width as lead volume — and the insight riding the thinnest ribbon: referrals are few but convert at 70%.

Maps to: the dp_attr first-party attribution cookie
bézier ribbonsdash reveal
No JS1.7 KB gzip
29

Per 100 customers

Every square is a person: 76 unchanged, 15 buying more, 9 shifting channel. The two-audiences rule as a visual — the same econometrics, told for a board.

Maps to: plain-language exec reporting
waffle gridgroup stagger
12 lines JS1.7 KB gzip
31

Stacked area mix

The ads layer visibly thickens while the total stays calm — the mix story a single revenue line buries. Layers share exact boundary vertices, so the stack can't gap.

Maps to: channel-mix analysis
clip-path wipeshared vertices
No JS1.7 KB gzip
33

Pareto 80/20

Ranked SKU bars plus a cumulative line, with the amber guide dropping onto the exact bar where 80% of revenue is reached. A budget decision, drawn.

Maps to: catalogue / SKU concentration
bars + cum. lineguide drop
No JS1.9 KB gzip
44

Treemap, area-true

Revenue by category where area is the encoding — six hand-squarified blocks, the one under review in accent. For when the parts are many and unequal.

Maps to: merchandising / revenue composition
rect pop-inhand squarified
No JS1.6 KB gzip

Uncertainty & rigour — the red-team, drawn

The standing methodology as visuals: intervals, jackknives, placebo distributions, detectability bounds. These are the charts that make a claim survivable.

24

Jackknife, leave-one-out

Drop each store in turn and re-estimate: every range clears zero, so no single store carries the claim. Small-n honesty, drawn instead of asserted.

Maps to: red-team default: small-n event studies
whisker growpooled diamond
No JS1.8 KB gzip
40

Placebo distribution

The estimator rerun on 800 windows where nothing happened — and the observed effect standing far outside what luck alone produces. A p-value as a picture.

Maps to: red-team: “test, don't debate”
bell path drawobserved drop-in
No JS1.6 KB gzip
37

Effects with detectability

Five campaign effects with 95% intervals — the ones crossing zero go grey AND carry their power bound, so “no effect” can never quietly mean “no power”.

Maps to: red-team default: effect ≈ 0 claims
CI whiskersverdict colour
No JS1.8 KB gzip
25

Forecast fan

History draws, then the forecast fans out in nested 50/80% bands — width is the honesty, and the decision rides the floor: the worst decile clears zero.

Maps to: cashflow scenario engine (finance)
nested bandsshared fork vertex
No JS1.7 KB gzip
23

Histogram with a threshold

Postcode-centroid error: most codes are fine, but a real tail sits beyond ±20 km — recoloured amber and shipped as an uncertainty column, never averaged away.

Maps to: geo / postcode uncertainty work
scaleY barstail recolour
No JS1.7 KB gzip
35

Fit with residuals

Dots first (the evidence), then the elasticity line draws through (the model), then grey residual sticks admit what it doesn't explain. Order of appearance = order of trust.

Maps to: decision modelling / price elasticity
dot→line→residualline draw
No JS1.9 KB gzip

Time, monitoring & ops — catching it the same day

Trends, seasons, breaches and progress. The recurring sell: the alert fires the day of the change, not at month-end.

20

Small-multiples sparklines

Eight stores on one shared scale — only the new store's catchment breaks trend, and it's the only card framed. Small multiples beat one crowded chart.

Maps to: store-impact per-store trends
shared y-scalespark draw
No JS1.9 KB gzip
26

Anomaly vs its band

The metric lives inside its expected band until it doesn't — one breach, one pulsing dot, one toast stamped 07:15, day of the change.

Maps to: the GMC feed monitor (timeline view of 10)
expected bandbreach pulse
No JS1.8 KB gzip
27

Control chart

The anti-panic tool: points inside the 3σ limits are weather, the one breach is climate. Stops teams chasing noise; makes the real signal unmissable.

Maps to: data-quality ops / exception rates
σ limitsbreach blink
No JS1.6 KB gzip
30

Calendar heatmap

Weekday rows × week columns keep time human: weekends run hot, and Black Friday week is visibly a different animal. A staffing decision at a glance.

Maps to: retail order patterns / ops planning
alpha cellsdiagonal wash
18 lines JS1.9 KB gzip
42

Cumulative steps

Money recovered pass by pass — recovery arrives in lumps and the step chart refuses to smooth that lie. Each riser is a named pass; the flag is R2.35m.

Maps to: the reconciliation engagement
step path drawriser dots
No JS1.7 KB gzip
38

Delivery timeline

Twelve weeks, four overlapping phases, one diamond on the cutover — the overlaps are the design: verification shadows migration, it doesn't follow it.

Maps to: the Snowflake migration plan
bar sequencemilestone pop
No JS1.8 KB gzip

Decisions & geography — the call, drawn

Where the analysis meets the decision: prioritise, choose, break even, and put customers on a map that routes like a car, not a ruler.

34

Effort × impact quadrants

Twelve audit findings placed by measured impact and estimated effort — the chart IS the prioritisation meeting, with the two quick wins ringed.

Maps to: audit → roadmap engagements
quadrant guidesring pulse
No JS1.8 KB gzip
39

Decision tree with EV

A pricing call made in rands: branches carry probabilities, leaves carry outcomes, the chosen path draws in accent — and the road not taken stays visible, dim.

Maps to: decision modelling engagements
branch draw orderEV badges
No JS2.1 KB gzip
45

Break-even crossing

Manual cost keeps climbing; the build starts higher and flattens. Where they cross is the only number the buyer needs — and the setup premium is drawn, not footnoted.

Maps to: automation ROI (recon engine, monitors)
two-line drawshared-vertex wedge
No JS1.9 KB gzip
36

Catchment dot map

Two stores, customers coloured by nearest store by road — the amber-ringed cluster is closer to A on a map but drives to B around the bay. No geodata, just geometry.

Maps to: geo / store-catchment routing
abstract dot mapbarrier arc
No JS2.0 KB gzip
41

Likert, diverging

Survey rows anchored on the same agree/disagree boundary so they compare instantly — and the one weak statement can't hide inside a stacked 100% bar.

Maps to: edtech survey work (parents / coaches)
outward growstraddled neutral
No JS2.0 KB gzip

Distributions & shape — what averages bury

Box, violin, ridgeline, beeswarm, butterfly, cycle: six ways to show the spread and the tail instead of the mean. The recurring sell: the SLA, the fairness question and the trend all live in the shape.

46

Box & whisker

Four carriers, near-identical averages, very different tails — and the SLA is set by the tail. The one outlier is a named incident, not noise.

Maps to: fulfilment / delivery SLAs
box growwhisker draw
No JS2.2 KB gzip
47

Violin distributions

The store channel is bimodal — gifting spike, big-ticket spike — so its “average basket” is a value almost nobody spends. Width IS density.

Maps to: order-value analysis by channel
mirrored pathclip wipe
No JS2.1 KB gzip
48

Ridgeline, hour × day

Seven overlapping ridges on one scale: weekdays twin-peak at lunch and evening, the weekend is one broad midday wave — a different roster, visibly.

Maps to: staffing / ops planning
area ridgesstagger rise
No JS1.8 KB gzip
49

Beeswarm

Every rep's pay change under the candidate scheme is one dot — the six give-backs stay visible people, not a footnote inside a mean.

Maps to: “Redesigning a sales commission scheme”
dot swarmpop stagger
No JS2.0 KB gzip
50

Butterfly pyramid

In-store grows left, online grows right, age bands down the spine — the channel crossover at 45 becomes a place on the chart, not a sentence.

Maps to: omni-channel strategy (store-impact)
mirrored scaleXdual origin
No JS1.9 KB gzip
65

Cycle plot

One panel per weekday, four weeks deep inside each: Friday and Saturday share a mean, but Friday is flat and Saturday is compounding +8% a week.

Maps to: retail seasonality / staffing
panel seriespanel means
No JS1.8 KB gzip

Composition, hierarchy & maps — parts of the whole, placed

Marimekko, sunburst, packed bubbles, tile cartogram, rose: area does the arguing, and each one carries its honesty note (√ radii, equal tiles) in the file.

51

Marimekko

Column width = province share, split = channel mix: “W Cape is majority-online AND a quarter of the business” lands in one shape.

Maps to: regional / channel strategy
width-true columnsstacked pop
No JS1.8 KB gzip
52

Sunburst

Category ring inside, subcategories outside, every arc a single dasharray circle — the whole hierarchy is dash arithmetic, no path math.

Maps to: revenue hierarchy (merchandising)
dasharray arcspathLength=100
No JS1.8 KB gzip
57

Packed bubbles

Tableau's signature view, hand-packed: radius = √share so area stays honest, and the long tail gets one collective label instead of eight unreadable ones.

Maps to: subcategory revenue at a glance
hand packingpop stagger
No JS1.8 KB gzip
67

Tile cartogram

Nine equal tiles in rough SA geography — a real map would drown Gauteng's 34% in the Karoo's empty space. Equal tiles let colour carry the number.

Maps to: provincial reviews
grid mapalpha encoding
No JS1.7 KB gzip
75

Rose (coxcomb)

Twelve wedges around the year clock, radius = √orders so area stays honest — used because December genuinely sits next to January's trough.

Maps to: seasonality, stock & staffing
sector paths√ radii
No JS1.9 KB gzip

Relationships & structure — what moves together

Chord, hexbin, parallel coordinates, network, dendrogram, SPLOM, correlation, connected scatter. The correlation family nominates hypotheses; the causal charts (06, 73) get to promote them.

53

Chord diagram

Ribbon width = customers shopping both categories; the bundling decision rides the fattest ribbon. Kitchen ⇄ Home carries 31% repeat traffic.

Maps to: basket / cross-shopping analysis
bézier ribbonsdasharray arcs
No JS1.7 KB gzip
56

Hexbin density

599K customers → 1,798 postcode points → 37 honest bins, one polygon stamped with <use>. Density without the overplotted dot soup.

Maps to: geo / catchment density
<use> gridalpha bins
No JS1.8 KB gzip
58

Parallel coordinates

Six stores, five shared axes, one shape each — and the airport store inverts on every axis. A per-metric averages table calls this portfolio “fine”.

Maps to: store portfolio reviews
polyline profilesoutlier accent
No JS1.7 KB gzip
59

Integration network

The retired point-to-point mesh stays faintly visible under the six governed spokes — the argument is the contrast, not just the tidy end-state.

Maps to: systems integration (recon, warehouse)
marching dashesnode pop
No JS2.0 KB gzip
60

Dendrogram

Stores merge right-to-left in similarity order; the dashed cut turns 8 stores into 3 archetypes — and the merge heights show how different they really are.

Maps to: store clustering / segmentation
elbow pathsmerge-order draw
No JS1.8 KB gzip
61

Scatterplot matrix

Every pairwise relationship at once — six panels from three dot clouds, because each mirror cell is the same <g> through a transpose matrix.

Maps to: CRM / loyalty metric trade-offs
transpose reusefinding frame
No JS1.9 KB gzip
62

Correlation matrix

Teal positive, amber negative, opacity = |r| — with the values printed, because a heatmap without numbers is a mood board. The finding: AOV ↔ Returns.

Maps to: channel & pricing diagnostics
diverging alphadiagonal fade
No JS2.1 KB gzip
63

Connected scatter

Price vs volume with time as the thread: three rises cost nothing, the fourth broke volume — the elasticity cliff has an address on the path.

Maps to: pricing / elasticity work
dated path drawamber break
No JS1.9 KB gzip

Time, finance & motion — density and drama, earned

Streamgraph, horizon, candlestick, bar race: the dense forms for walls of context, and the one animated format that earns its drama because the overtake is the finding.

54

Streamgraph

Ticket mix flows around a centre axis: feed & data queries swell after go-live while total thickness barely moves — a training gap, not an incident.

Maps to: migration / engagement health
shared verticesclip wipe
No JS1.9 KB gzip
55

Horizon strips

Six metrics × 36 weeks in 240 vertical px: values past the cap re-draw darker on top, negatives mirror up in amber. The two incidents are unmissable.

Maps to: ops monitoring walls
fold overlaymirrored negatives
No JS2.0 KB gzip
64

Candlestick cash

Weekly OHLC candles for the cash balance: every Friday close said “fine”, the wicks show two weeks kissed the overdraft mid-week. Range is the risk.

Maps to: cashflow memos / treasury
wick + bodyfacility line
No JS1.9 KB gzip
66

Bar race, no JS

Smart home climbs rank 5 → 1 across four quarters — lane hops and bar lengths on synced CSS keyframes with plateau holds, playing forward and back forever.

Maps to: category reviews (static twin: 21)
keyframe holdslane hops
No JS1.9 KB gzip

Boardroom & evaluation — decisions and models, audited

Combo, table lens, KPI cards, lift, survival, event study, tornado: what execs actually read, plus the charts that keep models and causal claims honest.

68

Dual-axis combo

Revenue bars, margin line: November's record bar sits on the line's floor — the promo bought revenue at 14% margin. Both axes zero-anchored and labelled.

Maps to: trading / promo reviews
bars then linehonest axes
No JS1.9 KB gzip
69

Table lens

Execs trust tables and skim charts — in-cell bars and delta chips give them both: exact rands stay auditable, magnitudes become impossible to misread.

Maps to: exec reporting (dashboards-as-code)
CSS grid tabledelta chips
No JS1.7 KB gzip
70

KPI cards

The BAN done properly: count-up numbers, sparkline context, and chips coloured by good/bad rather than up/down — falling returns stay teal.

Maps to: dashboard headers
count-upsemantic chips
14 lines JS2.2 KB gzip
71

Lift curve

The random-baseline diagonal draws FIRST — house rule: no score ships without beating the trivial baseline. Top 20% of the list captures 58% of conversions.

Maps to: red-team default: trivial baselines
baseline firstgain wedge
No JS1.9 KB gzip
72

Survival curves

Kaplan–Meier retention with censor ticks: recent joiners are counted for the months we saw them, never guessed beyond. Onboarding is +27pt at month 24.

Maps to: edtech / subscription retention
step pathscensor ticks
No JS1.9 KB gzip
73

Event study

Six pre-months of coefficients hugging zero, then the lift arrives and persists — parallel trends shown, not assumed. The chart the diff-in-diff (06) stands on.

Maps to: store-impact DiD (red-team)
CI whiskerspre/post split
No JS2.1 KB gzip
74

Tornado sensitivity

Each parameter swings the ROI while the rest hold at base, sorted by swing: adoption moves the case 3× more than build cost — de-risk that first.

Maps to: decision modelling (feeds 39)
centre-anchored barssorted swing
No JS1.8 KB gzip

Comparison & change, encored — same order, louder point

Charts you already know, re-cut so one thing lands harder — the turning point, the endpoint, the spread, the shift, the gap. Hover a card for ✎ Edit.

76

Annotated line

A trend line where the labelled turning points carry the story — each bend tagged with its cause. The line is context; the callouts are the argument.

Best for: exec narratives where every inflection has a reason to act on
line + areacausal callouts
SVG · no JS1.9 KB gzip
77

Lollipop ranking

A bar with the ink stripped out — thin stem, weighted dot — so the eye lands on the value, not the mass. Same order, far less clutter.

Best for: long ranked lists where full bars feel heavy or moiré
rankedstem + dot
SVG · no JS1.9 KB gzip
78

Cleveland dot plot

Two dots joined by a stick; the gap between them is the improvement, read without subtraction. Cleaner than paired bars, which double the ink.

Best for: two states across many categories where the change is the message
before / afterconnector = delta
SVG · no JS2.0 KB gzip
79

Radial bars

24 hours bent into a clock, so cyclical time becomes position — midnight meets midnight and the two daily peaks sit where intuition expects them.

Best for: naturally cyclical categories (hours, months, compass) or a punchy hero
polartiny JS gen
SVG · tiny JS1.8 KB gzip
80

Range bars

A floating bar from fastest to slowest with the median marked — the spread an average would hide, and the tail that makes a customer angry.

Best for: categories where spread matters as much as the centre — SLAs, price ranges
min–median–maxrisk flag
SVG · no JS2.1 KB gzip
81

Mix-shift ribbons

Two 100% stacked bars linked by ribbons that show the flow — how each channel's share redistributed, not merely that it changed size.

Best for: how a composition moved between two moments — channel, budget, portfolio
100% stackedflow ribbons
SVG · no JS1.8 KB gzip
82

Variance bridge

A waterfall that walks from budget to actual one driver at a time, decomposing a scary total into causes you can act on — and proving it lands on the endpoint.

Best for: explaining a gap between two totals — budget vs actual, plan vs forecast
waterfallrunning total
SVG · no JS2.0 KB gzip

Distribution & rigour, extended — the shape and the doubt

The red-team habit, drawn: show the whole distribution and the uncertainty, not a single point that pretends to certainty.

84

Density strip

A density curve with the raw rug beneath and both median and mean marked — the gap between them is exactly the skew an "average" buries.

Best for: one variable's shape, compactly, where a full histogram is overkill
density + rugmedian vs mean
SVG · no JS2.0 KB gzip
85

Q–Q plot

Sample quantiles against a normal line; here the points lift off in the upper tail — a fat right tail that would make naive confidence intervals too narrow.

Best for: checking a distributional assumption before you trust a model or t-test
quantile–quantilenormality
SVG · no JS1.8 KB gzip
86

ECDF with percentiles

An empirical CDF — every step a real order, so any percentile reads straight off the axis. No binning choices to argue about, unlike a histogram.

Best for: SLA and percentile questions — P50 / P90, "what share is below X"
step curveP50 / P90 guides
SVG · no JS1.8 KB gzip
87

Bland–Altman agreement

Difference vs mean with limits of agreement — two points breach them, both mis-posted to VAT clearing. The check that catches a right amount in the wrong account.

Best for: agreement between two systems measuring the same thing — SAP vs bank
difference vs meanlimits of agreement
SVG · no JS2.1 KB gzip
95

Marginal scatter

A scatter with a histogram on each margin — the relationship and both distributions in one frame, so a correlation driven by a clump can't hide.

Best for: when the relationship and each variable's own shape both matter
scatter + marginalstiny JS gen
SVG · tiny JS2.2 KB gzip
99

Forest plot

Each segment's estimate with its CI against a no-effect line; whiskers that cross zero are greyed, and a pooled diamond gives the honest bottom line.

Best for: comparing an effect across subgroups without cherry-picking
effect + CIpooled diamond
SVG · no JS2.2 KB gzip
100

Uplift with CI

Test-vs-control lift bars topped with confidence intervals; the one whose interval straddles zero is noise you shouldn't bank.

Best for: experiment / incrementality results where "beyond chance?" is the question
A/B lifterror bars
SVG · no JS1.9 KB gzip

Time, flow & monitoring — pace, pipes and staleness

Operational visuals for catching it the same day: are we on pace, where's the jam, and which feed just went stale.

88

Burndown

Remaining work against the straight ideal line; ride above it and you're behind, and the shaded gap is exactly how far.

Best for: tracking delivery against a plan — sprints, migrations, close checklists
ideal vs actualbehind-gap
SVG · no JS1.8 KB gzip
89

Cumulative flow

Stacked stage bands over time; a widening band is a bottleneck you can see before any table names it — here, the qualify stage.

Best for: flow systems where work moves through stages — pipelines, queues, WIP
stacked bandstiny JS gen
SVG · tiny JS2.1 KB gzip
90

Punchcard

Weekday × hour, each cell a dot sized by volume, so empty hours read as genuinely empty. The staffing question answered at a glance.

Best for: patterns across two cyclical keys where magnitude varies a lot
day × hoursize = volume
SVG · tiny JS2.0 KB gzip
91

Uptime ribbon

One bar per day, status-page style — 98% green with a lone red spike tells the reliability story faster than any number.

Best for: a compact history of a mostly-binary daily state — uptime, SLA, backups
status striptiny JS gen
SVG · tiny JS1.8 KB gzip
92

Deviation area

The space between actual and expected, filled and coloured by sign, so a soft dip reads as a shape you feel — not a wobble you squint at.

Best for: emphasising departures from a reference — vs budget, vs seasonal norm
signed fillline + area
SVG · no JS1.7 KB gzip
105

Freshness dots

Each source a dot on an "hours since update" track over green / amber / red zones — the bank feed at 26h and SARS at 70h are past their SLA.

Best for: monitoring recency across many feeds at once — pipelines, dashboards, backups
stalenessSLA zones
SVG · no JS2.0 KB gzip

Structure, decisions & finance — the call, the money, the data

Hierarchies and relationships placed to read; the trade-off and the money made explicit; the source profiled before you trust it.

83

Icicle hierarchy

A P&L read top-down as nested rectangular levels — fairer for small slices than a sunburst's wedges, and every level stays a scannable row.

Best for: a strict part-of-whole hierarchy read as levels, not rings
partitionlevels
SVG · no JS1.8 KB gzip
93

Arc diagram

Categories on one line, an arc per co-purchase; a stable, readable layout where a force-directed graph would wander into a hairball.

Best for: pairwise relationships over an ordered or small set of nodes
one-axis networkarc = strength
SVG · no JS1.9 KB gzip
94

Adjacency matrix

Co-purchase as a grid of sized dots; order the axes and the communities appear as bright blocks a network diagram would tangle into a hairball.

Best for: dense pairwise data where a node-link view overlaps itself
matrixreorder → blocks
SVG · tiny JS1.9 KB gzip
96

Proportional symbols

Bubbles sized by count on a schematic map, so a huge empty province can't shout as loud as a small dense one the way a choropleth would.

Best for: mapping a count across regions where area would mislead
bubble maparea = value
SVG · no JS1.7 KB gzip
97

Decision matrix

Options × weighted criteria as a red-amber-green table with a computed score, so "why not the cheap one?" has an auditable answer.

Best for: a defensible multi-criteria choice — vendor, tool, prioritisation
RAG tableweighted score
SVG · tiny JS2.2 KB gzip
98

Cost curve

Savings ranked cheapest-first, width = size and height = cost; everything left of the pay-back line funds itself before the expensive rebuild is worth a meeting.

Best for: prioritising a portfolio of actions by cost and size at once
width × heightpay-back line
SVG · no JS1.8 KB gzip
101

Cash runway

Balance projected to its zero-crossing at the current burn, with an act-by marker three months earlier — the real deadline, because decisions take time.

Best for: any "how long have we got?" — cash runway, inventory cover, headroom
line + areaact-by marker
SVG · no JS1.8 KB gzip
102

FX exposure

Each outflow stream split into its USD/GBP and ZAR parts, with a weighted headline — the foreign exposure to hedge.

Best for: a split-by-attribute that carries a risk reading — currency, fixed vs variable
split barsexposure %
SVG · no JS1.8 KB gzip
103

Tax ladder

The marginal step line and the smooth effective curve together — the gap is why "I'm in the 41% bracket" almost never means "I pay 41%".

Best for: any tiered / threshold schedule people confuse marginal with average
step functionmarginal vs effective
SVG · no JS1.7 KB gzip
104

Completeness grid

Field × source, coloured by completeness; the legacy import lights up red (41% postcodes, 12% VAT) before a join silently drops rows on it.

Best for: data-quality review before building on a source — the "profile first" habit
coverage heattiny JS gen
SVG · tiny JS1.9 KB gzip

Polynomial fits — the overfit trap, drawn honestly

Four ways a fitted curve can lie, and how to catch each: a curve that hugs every dot has memorised noise, a fit without a band hides its own doubt, residuals confess the shape a line missed, and no polynomial survives one step past its data.

106

Degree ladder

The same 15 points fitted at degree 1, 3 and 9. The degree-9 curve wiggles through nearly every dot — it has memorised the noise and will predict the next point worse than the straight line.

Best for: showing why a curve that hugs the data isn't a better model — the overfit made visible
polynomial fitoverfit
SVG · no JS2.8 KB gzip
107

Fit with confidence band

A cubic fit wrapped in a real 95% confidence band — computed from the fit, not drawn by hand. It's tight where the dots are dense and ~2× wider at the thin edges: how sure we are of the line, not where points land.

Best for: showing a fitted curve with how sure you are of it, widening where evidence thins
cubic fituncertainty band
SVG · no JS2.6 KB gzip
108

Residuals vs fitted

Fit a straight line to data that bends, then plot the residuals: if the line were right they'd scatter as flat noise, but instead they trace a clear ∪ — the curvature the fit never bent to catch.

Best for: the diagnostic a fit must survive — a pattern in the residuals means the wrong model
residual plotlack of fit
SVG · no JS2.1 KB gzip
109

Extrapolation cliff

One cubic, plausible among the dots — but the instant it leaves the data's support it shoots off the top on the left and the bottom on the right. Same coefficients, trustworthy inside, nonsense one step out.

Best for: the warning against reading any fit past its data — the tails whip out on cue
extrapolationout of support
SVG · no JS2.5 KB gzip

Interaction layer — zoom, build-up and the drill between charts

Two new runtimes make a static figure move with the reader. lib/dp-camera.js (~3 KB gzip) is a semantic zoom: it narrows the data window and re-ticks the axes rather than scaling pixels — and its DP.zoomThrough dives through one dot until it becomes the next chart. lib/dp-reveal.js (~2.5 KB) adds meaning a piece at a time — context, then colour, then the annotation. Every effect resolves to a clean rest state, so motion-off / a red-team probe lands on the finished chart.

110

Zoomable axes

Wheel into a 64-SKU cloud and the points don't blur — the data window narrows and the x/y axes re-tick for the range you're now in (R0–R620 becomes R280–R360, relabelled). Drag to pan, double-click to fly home. Semantic zoom, not a magnifying glass.

Best for: dense clouds where the structure hides in a clump — the axis tells you which slice you're in
DP.cameraDP.niceTickssemantic zoom
lib + 40 lines glue~3 KB gzip
111

Built a piece at a time

The frame, then the bars in neutral grey, then colour washes in to encode the verdict (teal beat plan, amber missed), then a band highlights where the misses cluster, then the floor month is named. Nothing new is drawn at the end — meaning is added, layer by layer.

Best for: presenting live, or any figure whose point is a relationship the reader won't see cold
DP.revealDP.colorizeDP.highlightBand
lib + scenes~2.5 KB gzip
112

Scatter → dot → pie

The clever link. Click a dot and the field scatters outward across the frame, the camera dives into that one point, and it blooms into a pie of that SKU's channel mix. The detail literally grows out of where you were looking, so you never lose your place.

Best for: dashboards with a natural overview→detail hierarchy — a market of points, each hiding a composition
DP.zoomThroughDP.scatterOutoverview → detail
lib + 50 lines glue~3 KB gzip
113

Over / under-indexed quadrant

A crosshair scatter, not a corner one: both axes cross at par (index 100), so position is the verdict — top-right over on both, bottom-left under on both, and the off-diagonal tension (big reach that won't convert; a small audience that converts hard). Dot area is spend. Click a point and it blooms into its two indices decomposed against the par line.

Best for: prioritisation & positioning — segments, SKUs, channels — where the real question is "which corner is this in?"
quadrant / index mapDP.zoomThroughoverview → detail
lib + 70 lines glue~3 KB gzip

Live data layer — the same idioms, from real CSV / JSON

Everything above is hand-authored geometry. lib/dp-bind.js (~2 KB gzip, zero dependencies) loads CSV/JSON, scales it, and emits the same primitives with the same classes — so the house animations apply unchanged and the annotations are computed: the 80% guide finds its own bar, the box plot derives quartiles from raw rows in the browser. Serve the folder (python3 serve.py) — these five fetch data.

L1

Pareto from CSV

Twin of 33, but the 80% guide drops on whichever SKU the cumulative share actually crosses — change the CSV and the argument re-derives itself.

Data: data/sku_revenue.csv (24 rows)
DP.bars + DP.linecomputed guide
lib + 30 lines glue2.3 KB gzip
L2

Revenue line from CSV

Axis labels from the data's own extent, peak annotation finds the real maximum month, draw-on and area wipe come free from the house classes.

Data: data/monthly_revenue.csv (24 months)
DP.area + DP.linecomputed peak
lib + 25 lines glue2.0 KB gzip
L3

Heatmap from CSV

168 cells from 168 rows, opacity = orders/max, and the busiest cell is found and ringed by the code, not the author.

Data: data/orders_by_hour.csv (7×24)
DP.cellscomputed ring
lib + 20 lines glue1.9 KB gzip
L4

Box plot from raw rows

The strongest case for the layer: 161 raw order rows in, quartiles/whiskers/outliers computed in-browser by DP.box — nothing pre-aggregated to drift stale.

Data: data/delivery_days.csv (raw orders)
DP.box + quartilesoutliers surfaced
lib + 25 lines glue2.0 KB gzip
L5

KPI cards from JSON

The whole header row — labels, count-ups, good/bad chips, per-series-scaled sparklines — from one JSON file. Point it at an API route and it's production.

Data: data/kpis.json
DOM templatesemantic chips
lib + 30 lines glue2.3 KB gzip

Narrative layer — scenes, themes and the honest lie

lib/dp-story.js (~2 KB gzip on top of the data layer) turns a chart into a told story: named scenes stepped by buttons / arrow keys / autoplay, each re-aiming colour, glow and dimming at a different part of the same picture, with captions whose numbers are computed from the data. Client theme packs re-brand everything live. And scenes may lie — marked deceptive — but the runtime refuses to run unless a reveal scene follows and confesses every trick. Serve the folder (python3 serve.py).

S1

The narrated Pareto

Five scenes over one live-data chart: catalogue → curve → head → tail → decision, emphasis moving with the argument. Four buttons re-theme the whole page per client.

Shows: scene control · focus/dim · count-ups · theme packs
DP.storyDP.focusDP.applyTheme
lib + scenes4.1 KB gzip
S2

Three lies, one dataset

Truncated axis, cherry-picked window, salience rigging — three “true” scenes that mislead, then the frame morphs back to honest and the runtime confesses every trick it used. Delete the reveal and the story refuses to run.

Shows: the disclosure gate — deception only ships with its confession
affine re-framingdeceptive:trueauto-confession
lib + scenes5.2 KB gzip
S3

Does the lead scorer work?

A real backtest told as six scenes: 363k leads, the claim, the strongest objection tested head-on (new customers only), the diluted-label catch, a rule-by-rule scoreboard with the two broken rules, then the decision. Captions compute from the backtest’s own JSON — the monthly re-run retells the story.

Shows: devil’s-advocate scenes · view morphing (bars → waffle) · red-team verdicts in the data file
DP.storyDP.wafflecomputed captions
lib + scenes5.3 KB gzip
S4

Do stores eat the website? · board cut

The cannibalisation answer for a boardroom: statistical twins, a per-100-customers waffle that re-lights per scene (+14 kept, 69 discovering), honest BANs that print the median next to the mean, and the neighbourhood check.

Story 1 of 3 on one analysis: same evidence, exec framing (two-audiences rule)
DP.waffleDP.panelcount-up BANs
lib + scenes4.2 KB gzip
S5

The event study, walked

The technical cut: 48 real months of gap-vs-twin, scenes in the order of trust — parallel pre-lives, the amber anticipation tell at month −1, the persistent post gap, then +R599 that RISES to +R893 when you attack it.

Story 2 of 3: same evidence, econometric framing · guides computed from the data
real seriescomputed guidesBAN morph
lib + scenes4.7 KB gzip
S6

The cross-examination

The adversarial cut: four attacks (mean reversion, reverse causality, tail artifact, hidden cannibalisation), each answered by the falsification test that actually ran and stamped with a verdict chip — plus the board showing the claims that LOST.

Story 3 of 3: same evidence, red-team framing · incl. the rigged test we caught
DP.whiskersverdict chipsDP.panel
lib + scenes5.2 KB gzip
S7

Where did R3.5m go?

The detective structure: six suspects examined and stamped ✗ (each with the stat that clears it), the basket clue, geography cracking it, the decomposition waterfall with its uncertainty bracket drawn — and a finale that prints the dead-end ledger AND the still-open threads.

Shows: the dead-end ledger — ruled-out hypotheses are findings, carried by the artifact
deadEnd ledgersuspect stampsuncertainty bracket
lib + scenes6.7 KB gzip
S8

The Spotlight said “flat”

A monthly finance report as a told story, generated by the TM2 finance-engine: the flat total, the mix shift beneath it, the EBITDA bridge, the per-lesson unit economics (price, not volume), the accountant’s open question answered with a ratio — and the reconciliation gates printed as the final scene.

Shows: reports that regenerate themselves — captions computed from engine JSON, gates travel with the data
finance-enginewaterfall bridgeunit economics
lib + scenes~6 KB gzip
S9

One dataset, repurposed

The same 24 SKU bars fly into a stack, unroll into the cumulative curve, straighten toward the line of equality (one curve bending into another), fan out as KPI cards, then snap back — literally the same ink each time. A planner picks each element's journey by shape; each scene pinpoints its finding.

Shows: adaptive scene-to-scene transitions — magic move that reads by shape, not one global effect
DP.transitionpoint-morphDP.pinpoint
lib + scenes~7 KB gzip
S10

Zoom until the dot becomes a chart

A market of SKUs is a field of dots — but each dot is a door. Dive into the best-seller and it blooms into its channel pie; read the mix, then dive again into the dominant wedge for that channel's twelve months; then pull all the way back. Overview → detail → deeper → overview, as one motion.

Shows: the drill as narrative — scale changes as scenes, each an ordinary rest state so motion-off still lands
DP.zoomThroughDP.storyoverview → detail
lib + scenes~7 KB gzip