Workflow: data story (animated charts and numbers)
Read this when the video is about numbers: a metric that changed, a benchmark, survey results, a
report's key figures, an animated chart ("turn these results into a 20 s video", "animate this CSV").
The default build is the data template: charts read JSON files, so the numbers never live in HTML.
See it done: 07 Data story · 12 Energy report
Essentials#
- Defaults: 15-30 s, 16:9 at 1920x1080,
editoriallook, a restrainedunderscorebed or none, at most 2-3 soft effects, one insight per chart state, the takeaway as the chart title, the source on screen in the last scene (§ Defaults) - Every number on screen matches its source exactly (round only in narration); never extrapolate, smooth or fill gaps; if the data does not support the takeaway, say so before building (§ Honesty first)
showtime job init <topic>-data --goal "...";showtime data inspect <table>; write the takeaway as a sentence with a number plus the source line in SHOWTIME.md (showtime job note --verified) (§ Steps)showtime new data <job>/project --title "..." --duration <len>; charts read JSON in<job>/project/data/, numbers never live in HTML (§ Steps)showtime data import <table.csv> <job>/project --x <col> --y <col> --scene bars --title "<takeaway>"(--chart bar|hbar|line|race,--series,--where,--top N,--names); anything derived is a small script kept in the job (§ Steps)"count": falsein the chart JSON for a number a paused frame must never misstate; replace every placeholder and the source line; 30 s or more gets 2-3states, not a stretched--duration(§ Steps)- Voice: one line per chart state, numbers spelled out in the script and exact on screen, each revealed on its word. Sound: never a drop, claps or a bright melody under numbers (§ Steps)
showtime check <job>/project,showtime look <job>/project, show the sheet with the numbers listed; final with"poster","expect": {"must_show": ["74%"]}andshowtime render <job>/project --job <job>, thenshowtime checkandshowtime qa <job>, then the critic round it names (quality mode, the default) (§ Steps)- Hold a settled chart 2-3 s; direct labels, not legends; custom labels 29 px or more (§ Pitfalls)
- Pacing: a visible change about every 2 s (a state, a callout or reference line arriving, a count-up); hold
only as long as the text needs to be read. Fix
slow_scene,dead_airand qafrozenwarnings (a beat, or a shorter scene), never accept them; a slow push-in alone still plays slow (§ Pacing) - Items that arrive in a later state: leave them out of the earlier states (or
null), never0; never hide chart labels while a chart moves (chart_labels_hidden); a callout names its year or value (§ Pacing)
| Section | Lines |
|---|---|
| Inputs | 46-50 |
| Defaults | 52-58 |
| Honesty first | 60-64 |
| Steps | 66-136 |
| Pacing | 138-159 |
| Pitfalls | 161-168 |
| Read next | 170-173 |
Inputs#
- The data itself: a CSV, JSON, spreadsheet export, a table in a README or report, with its source.
- The takeaway the user wants (or you propose one from the data and state it as an assumption).
- Helpful: brand kit, platform, whether there is a voice-over.
Defaults#
15-30 s, 16:9 at 1920x1080 (--aspect 9:16 and 1:1 also pass check); editorial look from the
template; a restrained underscore bed (sections intro/verse/outro, no build or drop) or no music,
and at most 2-3 soft effects (music.md section 1); one insight per chart state; the takeaway as the chart title; the
source cited on screen in the last scene. State the takeaway you found in the opening line; ask which
matters most only when the data supports two different stories.
Honesty first#
Every number on screen must match its source exactly (round only in narration). Never extrapolate, smooth, or fill gaps; never start a bar axis above zero without saying so; label sample or illustrative data as such. If the data does not support the takeaway the user wants, say so before building.
Steps#
-
Job.
showtime job init <topic>-data --goal "...". -
Read the data. Load it, check units, time ranges, missing values and outliers. A CSV, TSV or JSON table:
showtime data inspect <table>lists its columns, which are numbers, and the first values. Write the takeaway as a sentence with a number ("Build time fell 74 % in six months") and the source line. Done when: the takeaway and its source are in SHOWTIME.md (showtime job note --verified). -
Story. Headline takeaway, context state, the change, one annotation, the implication (
story.mdsection 4, data story). Pick the chart per idea: bars to compare, ranked bars to re-order, lines for change over time. Plan chartstatesfor before/after moments. -
Project.
showtime new data <job>/project --title "..." --duration <len>(scenesopen,bars,lines,stat). Charts read one JSON file each in<job>/project/data/. From a table:showtime data import <table.csv> <job>/project --x month --y signups --scene bars --title "Signups doubled after launch" --highlight max --annotate "launch week"It writes
data/<table>.jsonand points that scene's chart at it (data-src,data-type).--chart bar|hbar|line|race(default: bar for one value column and up to 12 rows, else line),--y a,bfor several lines,--series <col>for long tables (one row per label and series),--top N(keeps the N largest; a bar chart keeps the table's order, e.g. chronological, an hbar ranks by value;--sort value|sourceoverrides),--ref next(a dashed line at the first row--topleft out: "next warmest"),--step S(race, seconds per row),--prefix/--suffix/--decimals(detected from values like $3.5k or 45 %),-o <file>,--dry-run. Negative values (anomalies, deltas) chart below a zero line.--names col="Label"sets a series' display name (the default only makes the column readable:wind_solarbecomes "Wind solar"; custom components keyed by name see the display name). A scene without a chart element still gets its data file; the import prints the element to add.A long table (a statistics download: one row per series and month, with annual rows mixed in, like EIA's
MSN, YYYYMM, Value): reduce it in the import, no derive script.--wherekeeps the matching rows (repeatable, all must match):COL=VALUE,COL!=VALUE,COL~REGEX,COL>N. Then--seriesturns one row per (label, series) into lines or racers:showtime data import eia.csv <job>/project --where "YYYYMM~13$" --where "MSN~^(CLETPUS|NGETPUS|WSETPUS)$" \ --x YYYYMM --y Value --series MSN --chart line --names CLETPUS=Coal,NGETPUS="Natural gas",WSETPUS="Wind + solar"(EIA marks annual totals as month 13.)
showtime data inspect <table>first shows the columns and sample values. Anything more (a ratio of two series, a rolling mean) is a small script that writes a clean CSV; keep it in the job so the numbers can be checked.For a number a paused frame must never misstate, set
"count": falsein the chart JSON (labels appear at their final value). The subtitle names the source file until you write the real source. Other charts: write the JSON by hand (title= the takeaway,subtitle,prefix/suffix,highlight,annotate,data,states;components.mdsection 4). Replace every placeholder value; update the closing number and the source line. For 30 s or more give the bar scene 2-3states(a before/after):--durationfar past the template's length stretches scenes into still holds, and it warns past 1.5x. -
Voice (optional). One line per chart state,
## <scene id>per scene in<job>/project/narration.md;showtime voice script ... --fit <seconds> --estimatefirst (cut words until it fits), then the same command without--estimate, andshowtime retime <job>/project --from-voice <job>/project/voice/timeline.jsonas insocial-short.mdstep 4; reveal each number on the word that says it. Numbers are spelled out in the script ("seventy-four percent"), exact on screen. -
Sound. Keep the calm bed (never a drop, claps or a bright melody under numbers; a team report can also go without music); one soft effect on the closing number (
count-up.sync.landgives the exact landing time), at most two more on real state changes. Transitions: one calm family for the whole piece,dipbetween scenes full of text (transitions.mdsection 3, data and reports). -
First look.
showtime check <job>/project,showtime look <job>/project(looking.md), judge every settled chart state: labels readable, the highlight obvious, end labels not clipped. Show the sheet with the numbers listed beside it so the user can verify them, and carry on. -
Final.
"poster"on the most telling chart state;"expect": {"must_show": ["74%"]}for the key figures;showtime render <job>/project --job <job>. -
Verify.
showtime check <job>/project(records on-screen text formust_show), thenshowtime qa <job>(the latest final),showtime look <job>, then the critic round qa names (quality mode, the default:review.md). Done when: PASS/WARN with every must-show figure found and no "review pending". -
Deliver. Share copy that states the same number and the source; exports; the delivery card.
Pacing#
- A beat about every 2 s. After the marks land, the next thing arrives: a reference line, the
callout (
annotate.atabout 1-1.5 s after it), a highlight (highlightAt: "settled"), the nextstate, a count-up. A chart scene longer than about 6 s needs a second state or a split into two scenes.showtime checkreportsslow_scenewhen a scene holds past its last change and its reading time by more than 2.5 s (4 s for the last scene): add the beat or shorten the scene (data-dur, orshowtime retimefor the whole video). The template's slow push-in keeps a hold from freezing; it does not make it lively. - Reading time sets the hold, not more. Every text stays up for its reading time (the phone check's
short_text); a longer hold than that and the next beat's 2 s is dead time. A 30-45 s story is 5-7 scenes or 2-3 states per chart, not the four template scenes stretched. - Constancy. Items that join in a later state are absent before it: leave the label out of the
earlier state's data (or give it
null); a0is a real zero with a "0.00" label on the baseline. Existing labels stay lit and ride their bars while items arrive;count: falselabels show only real values, also across a morph. Never add CSS that hides.st-chart-valor.st-chart-endlabelwhile[data-st-moving](check warnschart_labels_hidden): every number blinks off and on. - Callouts name their datum. "2015: first year above 400 ppm", not "first year above 400 ppm". On a
line chart the chart prefixes the point's label when the text does not name it;
{label}and{value}inannotate.textare filled from the data ("{value} in {label}"). - Transitions between text-heavy scenes:
dip. A blur dissolve smears both scenes' text in the middle of the window (a muddy frame on a paused player); the template uses dips throughout.
Pitfalls#
- Several insights in one chart state: split them into states or scenes.
- Legends instead of direct labels; rainbow palettes. Highlight one series, mute the rest (
color.md). - Holding the settled chart for less than 2 s: nobody reads it (2-3 s, longer when narrated).
- Axis labels too small at 1080p: the chart keeps them at 29 px or more; custom labels you add need the
same (check groups its
small_textnotes into one line). - A count-up that lands on a rounded value different from the source.
Read next#
references/story.md, references/components.md, references/color.md, references/typography.md,
references/pacing.md, references/qa.md.
View or edit skills/showtime/references/workflows/data-story.md on GitHub