calesthio/d3-animated-data-visualization
Production guidance for converting sourced data and approved claims into truthful, accessible, deterministic animated visualizations with D3. Use for data-driven charts, maps, networks, hierarchies, transitions, annotations, responsive video variants, frame-by-frame browser rendering, and visualization QA; not for generic dashboards, unsourced decorative charts, or ordinary motion graphics without a data contract.
npx skills add https://github.com/calesthio/generative-media-skills --skill d3-animated-data-visualization
Use this skill when data determines the marks, scales, layout, labels, and animation of a rendered-media sequence. D3 is useful when standard chart components cannot express the required encoding or choreography with sufficient control.
The job is not to make numbers move. The job is to preserve meaning while controlling attention over time. Source integrity, denominator, units, uncertainty, scale, and transformations are production inputs, not footer decoration.
D3 packages and APIs are versioned independently. Facts were checked 2026-07-12. Record exact module versions and re-check official documentation when the installed set differs.
Use D3 for:
Prefer a standard chart component when it already supports the truthful chart, labeling, accessibility, and export needs. Prefer a motion compositor when data is already reduced to a few approved values and no data-driven layout remains. Do not use D3 to lend false authority to illustrative or invented numbers.
Before selecting a chart, record:
If these fields cannot be resolved, label the output as exploratory or block publication. Do not silently repair missing data, infer denominators, or choose a favorable date range.
Choose the chart from the comparison task:
| Question | Typical starting point | Main integrity risk |
|---|---|---|
| Compare categories | ordered bars or dot plot | truncated length baseline, unstable ordering |
| Show change over time | line, step, area, or small multiples | smoothing implies observations, missing intervals hidden |
| Show distribution | histogram, strip/dot, box, density | bin/bandwidth manipulation, hidden sample size |
| Show relationship | scatterplot with annotation | correlation implied as causation, overplotting |
| Part-to-whole | stacked bars or carefully labeled areas | totals not equal, small segments unreadable |
| Geography | projected map plus normalized measure | raw counts mistaken for rates, projection distortion |
| Hierarchy | tree, treemap, partition | area/angle hard to compare, hierarchy ambiguity |
| Network/flow | node-link, Sankey, chord | layout mistaken for geography or importance |
Production heuristics:
Do not smooth a line merely because it looks polished. Curves can overshoot or imply unmeasured intermediate behavior. Use linear or step interpolation when that better represents the observations; explain any smoothing method.
Documented fact: D3 scales map a domain to a range. D3 provides continuous, sequential, diverging, quantize, threshold, ordinal, band, point, time, and other scale families. Axes render human-readable ticks from scales. Shape generators convert data into path geometry.
Implementation contract:
Example scale setup:
const x = d3.scaleBand()
.domain(data.map((row) => row.category))
.range([plotLeft, plotRight])
.padding(0.22);
const y = d3.scaleLinear()
.domain([0, d3.max(data, (row) => row.value)])
.nice()
.range([plotBottom, plotTop]);
This is an example for positive bar magnitudes, not a universal scale recipe.
Documented fact: selection.data(data, key) binds data to selected elements. selection.join() can handle entering, updating, and exiting elements. Without a key, matching is by index.
Use a stable semantic key so a mark represents the same entity across states:
const marks = plot.selectAll("rect.bar")
.data(rows, (row) => row.id)
.join(
(enter) => enter.append("rect").attr("class", "bar"),
(update) => update,
(exit) => exit.remove(),
);
Index keys can make bars appear to transform into unrelated categories after sorting or filtering. Test additions, removals, ties, and reordered categories.
Each animation beat should answer one of these:
Recommended reveal order:
Avoid animated counts that imply measurement precision the source does not support. Do not overshoot quantitative marks with spring/elastic easing: a bar temporarily exceeding its value visually states a false quantity. Easing may control reveal progress, but the held mark must encode the exact value and misleading intermediate states should be avoided.
Documented fact: D3 transitions are scheduled over elapsed time using D3's timer system. They suit interactive browser animation, but a video renderer may request frames out of order or without real-time playback.
For fixed media, calculate visual state directly from the requested frame:
function renderFrame(frame, fps) {
const raw = clamp01((frame - startFrame) / durationFrames);
const progress = d3.easeCubicInOut(raw);
bars
.attr("y", (row) => d3.interpolateNumber(y(0), y(row.value))(progress))
.attr("height", (row) => d3.interpolateNumber(0, y(0) - y(row.value))(progress));
}
Better still, prebuild interpolators when data is frozen. Use explicit start/end state for entering, updating, and exiting marks. Derive annotations, axis transitions, labels, and source notes from the same frame state.
Requirements:
For a force layout, run a fixed number of ticks with fixed initial positions and random source, store the resulting coordinates, then animate from those frozen coordinates. Do not run an unconstrained live force simulation independently in every render worker.
Design each output from the same data/claim contract but recompute layout for its canvas.
Documented facts from WCAG 2.2: color cannot be the only visual means of conveying information; normal text generally needs at least 4.5:1 contrast and large text at least 3:1; meaningful graphical objects and states generally need at least 3:1 non-text contrast against adjacent colors.
For every visualization:
For interactive publication, provide keyboard and screen-reader semantics appropriate to the host and a non-visual equivalent. For prerecorded video, narration, captions, transcript, and an accessible data table are the practical alternatives. Sonification can supplement, but does not automatically replace, a structured data alternative.
Use SVG when inspectable vector marks and text are valuable and DOM size is manageable. Consider Canvas for very dense marks, but plan accessible equivalents and crisp text separately. Reduce data only with a documented method that preserves the claimed pattern.
Measure:
Do not set universal point-count thresholds. The chart type, browser, renderer, hardware, and effects determine the actual limit.
Keep a visualization manifest containing:
QA passes:
Critical failures include an unsupported claim, omitted source or denominator, deceptive scale, incorrect totals, uncertainty hidden where consequential, color-only meaning, nondeterministic layout, or source notes unreadable in the delivered version.
This is a complete example, not a mandatory formula.
Intent: create a seven-second 9:16 evidence card for an approved statement: "Pilot teams recorded 31% fewer manual escalations."
Data contract: internal pilot; eight teams; measurement period April-June 2026; comparison is the same teams' January-March 2026 workflow; data accessed 2026-07-05; value is a relative reduction; source is not a randomized study; exact counts and approved disclosure are supplied.
Design: two horizontal bars with a zero baseline: Prior workflow and Pilot period. Direct labels include counts; a separate 31% fewer annotation states the relative comparison. Source and sample note remain visible during the final two seconds. Color is reinforced by labels and different hatch/solid treatments.
Animation:
00:00.00-00:00.60 Question, axis, unit, and baseline appear.
00:00.60-00:01.50 Prior bar grows linearly to its exact count.
00:01.20-00:02.10 Pilot bar grows to its exact count.
00:02.20-00:03.00 Difference bracket and "31% fewer" annotation appear.
00:03.00-00:05.00 Hold comparison.
00:05.00-00:07.00 Hold comparison plus source/sample disclosure.
Evaluate every mark from frame-derived progress; do not use elastic overshoot. Export the underlying two-row table with the delivery.
Expected result: exact values, fair length comparison, clear denominator/timeframe, readable source, and identical frames across clean renders.
Likely failures: animating 31% from zero without showing the compared counts; omitting that this is an internal pilot; truncating the bar baseline; source visible too briefly; color-only distinction.
Variation: if exact counts cannot be published, use a clearly labeled indexed comparison (prior workflow = 100) and explain the transformation instead of fabricating bar values.
This is a complete example, not a mandatory formula.
Intent: make a 20-second documentary insert showing standardized precipitation anomaly across watersheds and then comparing the five most affected basins.
Data contract: named meteorological dataset/version; watershed geometry source/version; monthly anomaly relative to a stated 30-year baseline; missing stations and interpolation method documented; values are anomaly scores, not raw rainfall.
Workflow:
Expected result: viewers see the spatial pattern, then receive a precise ranked comparison without mistaking map area for anomaly magnitude.
Likely failures: raw station count drives color; no-data regions look like normal; projection is unstated; interpolation is presented as direct observation; live force/collision labels differ between workers; map-only delivery lacks an accessible table.
Variation: for a short social cut, omit monthly animation and use one approved month plus the ordered dot plot; preserve the baseline and caveat.
Official and authoritative sources checked 2026-07-12:
Take calesthio/d3-animated-data-visualization from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.