Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Race charts — paste a table, get a race

The charts kit turns a table into an animated race chart — ranked bars, columns, or lines that reorder over time as the numbers change. It’s the viral data-viz format (GDP, population, brands, sports…), but true to the data and self-contained in one script: no timeline scrubbing, no keyframes.

Three ideas:

  1. Declare a chart — racechart(id, layout, periods)
  2. Paste the data — racedata(id, "…")
  3. Play it — race(id, seconds)
canvas("16:9"); template("blank");

racechart(gdp, "bar", "2000 2010 2020 2024", "GDP by Country ($T)");
racedata(gdp, "
  USA,   us, 10.3, 15.0, 21.4, 27.4
  China, cn,  1.2,  6.1, 14.7, 20.5
  Japan, jp,  4.9,  5.8,  5.0,  4.2
  India, in,  0.5,  1.7,  2.7,  3.9
");
race(gdp, 8);

The bars grow, reorder as ranks change (the reorder is the race), the axis rescales with the running max, and the year + values tick — all from that block.


The data block

racedata(parent, "block") is the heart of it. One row per entity:

label  [icon]  v0  v1  v2  …        ← one value per period

The parser is forgiving so real data pastes straight in:

  • Rows split on a newline or a ;.
  • Cells split on comma, tab, or whitespace — so a CSV, a TSV, or a spreadsheet copy all work.
  • Missing cells count as 0 (e.g. a company/party that didn’t exist yet).
  • Multi-word labels are fine when the data is comma- or tab-delimited.

Prefer to build rows in code? raceseries adds one entity at a time — the computed/loop path:

for i in 0..n {
  raceseries(g, name{i}, "rocket", vals{i});   // label, icon, values
}

Icons — any SVG on the bar

The optional icon column puts a flag, logo, or emoji on each bar. It takes any SVG, not just flags:

You writeResolves to
usa flag shorthand → asset:svg/flags/us.svg
rocket, heart, trophyan emoji / icon alias
asset:svg/lucide/lightbulb.svgan explicit set path
logos/acme.svgyour own file (any path ending .svg)
(omit it)the row is just label + values

Icons are imported as real geometry via svg(), so they recolour and scale cleanly. Browse every bundled asset in the SVG catalogue.

A handful of flags whose stars use SVG <use>/<marker> (e.g. China) render imperfectly for now — most flags and all icons/emoji are clean.


Layouts

racechart(id, layout, …) takes one of three:

  • "bar" — horizontal bars, ranked top-to-bottom. The classic; gets value gridlines + a live Total readout.
  • "column" — vertical bars, ranked left-to-right.
  • "line" — each series a line that draws on over time; the race is which line climbs highest (fixed axis, tip labels auto-declutter).

Bar + line, together

On a bar race you can overlay a companion line:

  • raceline(parent) — a line across the top showing a running total (auto-summed from the bars), or raceline(parent, "label", "v0 v1 …") for an explicit metric.
  • racepanel(parent) — a multi-line history panel below the bars: every series as a line revealed up to a moving time-cursor (the Flourish bar+line combo).
racechart(de, "bar", "1949 … 2021", "German Elections %");
racedata(de, " CDU/CSU, , 31, … ;  SPD, , 29, … ; … ");
racepanel(de);        // history panel below the bars
race(de, 16);

Composing with the rest of manic

A race chart is just tagged entities, so it drops into any scene — a voiced Creator Short, a quiz reveal, a caption sequence. In creator-race-quiz a quiz poses a question and the race chart proves the answer, narrated with speak, closed with a CTA card — the chart running inside a par while the voice-over plays over it.


Examples