Sometimes it's better to be discrete
When dots, icons, and repeated signs beat bars, blobs, and smooth densities.
Should I leave now or wait for the bus?
estimate it in panel 01 first
of 100 commuters make the connection
minutes, kept as measurable length
Area is the answer
A density curve represents the probability correctly, but the reader must estimate a shaded area. Commit to a guess before revealing the count so you can compare the two tasks.
50 possible arrivals
Kay and Hullman's quantile dotplot: each dot is one equally likely arrival, stacked so interval probability becomes literal counting.
Live results from diagnoseTokenEncoding for exactly the encoding rendered in panel 02 — sabotage it and the critique updates with it.
No warnings. The tokens declare their semantics, the count strategy matches the meaning, and 50 visible tokens is within budget.
Risk is easier as people
The same threshold, translated: out of 100 commuters making this exact choice, the highlighted people miss their connection. Fixed denominators keep percentages honest and countable.
One possible morning at a time
Hypothetical outcome plots show one possible result at a time. Watch the simulated mornings accumulate, then compare them with the quantile dotplot in panel 02.
Combine a precise scale with countable signs.
The bar gives the expected wait a measurable length. Each bus sign on top represents two minutes, including a partial sign at the end. The values under the axis show the total represented.
Quantile dotplot
This task asks readers to estimate probability from a distribution. Quantile tokens turn probability mass into countable possible outcomes.
- Token type
- dot
- Semantics
- possible-outcome
- Count
- quantile
- Layout
- dotplot
The recommendation is deterministic: same task, same suggested encoding. API names for wiring it up live in the implementation section below.
ISOTYPE
Repeat a sign, never grow it — quantities become countable things.
Natural frequencies
Show risk as 18 visible cases among 100 commuters.
Hypothetical outcome plots
Uncertainty as possible worlds, drawn one at a time.
Quantile dotplots
“When(ish) is my bus?” — a distribution you can count.
Pictograms, infographics, icon arrays, and uncertainty displays share a useful move: replace an abstract magnitude with things a reader can count—units, cases, and possible worlds.
TokenLayer generates the countable marks.
Semantics first, then placement, then marks: generateTokens decides what each token means, the dotplot layout Wilkinson-stacks the quantiles, andtokenLayer emits ordinary Semiotic scene nodes with the same canvas, SVG, hover, focus, and accessible-table machinery as every other custom chart.
import { XYCustomChart } from "semiotic/xy" import { diagnoseTokenEncoding, generateTokens, suggestTokenEncoding, tokenLayer, } from "semiotic/recipes" // 1 · Say what a repeated mark MEANS before anything is drawn. const encoding = { tokenType: "glyph", icon: "bus", // the built-in ISOTYPE bus sign tokenSemantics: "possible-outcome", countStrategy: "quantile", // Kay/Hullman: equally likely arrivals tokenCount: 50, layout: "dotplot", // Wilkinson-stacked quantile dotplot } // 2 · The records carry the ledger and a live design critique. const tokenSet = generateTokens(arrivalSamples, encoding) tokenSet.diagnostics // e.g. TOO_MANY_VISIBLE_TOKENS if you ask for 800 // 3 · Any custom layout stamps them as real scene nodes — canvas + SVG, // hover, keyboard nav, transitions, accessible rows, for free. function busDotplotLayout(ctx) { const { height } = ctx.dimensions.plot const layer = tokenLayer({ input: tokenSet, options: { tokenSize: 17, cellHeight: -19, // negative step: stack up from the axis y: height - 32, valueToX: (arrival) => ctx.scales.x(arrival), color: (t) => (t.sample <= threshold ? "#236d99" : "#c93d3d"), datum: (t) => ({ arrival: t.sample, quantile: t.quantile }), pointId: (t) => `outcome-${t.index}`, }, }) return { nodes: layer.nodes, overlays: <ThresholdLine /> } } <XYCustomChart data={arrivalData} layout={busDotplotLayout} xExtent={[0, 22]} /> // 4 · Or ask for the encoding first — task in, semantics out. suggestTokenEncoding({ taskIntent: "estimate probability", dataType: "distribution" }) // → { recommendedEncoding: "quantile-dotplot", tokenEncoding, rationale, … }