---
name: "animated_infographic"
description: "Turning structured data into animated infographic GIFs and short clips: timing each reveal, placing labels so they stay readable, checking representative frames, and export specs per platform. Use when asked to animate a chart, make a data GIF, build an animated infographic, or turn numbers into a short looping clip."
---

# Animated Infographic

## Purpose
Turn verified numbers into a short looping animation that a viewer can read without pausing it. The skill ships a tested rendering pipeline: a JSON plan in, a GIF and an MP4 out, with a frame QA pass that checks size, dimensions, contrast, label fit, and representative frames before anything ships.

## Workflow
1. **Verify the data first.** Every number in the plan must come from a source the user can check. Put the source in the plan `source` field, it prints on every frame. If a number cannot be verified, cut it or label the piece as illustrative, the way `assets/sample-plan.json` does. Never use the sample numbers as real data.
2. **Write the plan.** One JSON file per piece, following `assets/sample-plan.json`. Required: `title`, and `steps`, where each step has a `heading` and `bars` with `label` and `value`. Keep 2 to 5 steps and 2 to 5 bars per step. Bar labels and order stay the same across steps, a new bar enters from zero. Add `unit` and `value_format` when values need them, for example `"{:.1f}"` with unit `"%"`.
3. **Time the reveals.** Follow `references/timing-and-layout.md`. Defaults in the renderer are a 1.0s first hold, 0.7s transitions, 0.8s between steps, and a 1.5s final hold. One change per step, bar positions never move, bars scale to the largest value across all steps.
4. **Pick the canvas from the destination.** Square 1080x1080 for feeds and GIPHY, vertical 1080x1920 for Reels, Shorts, and TikTok, 640x640 for email. Full targets per platform are in `references/platform-specs.md`. Width and height must be even.
5. **Render.** Run `bin/infographic.py plan.json --both out/name` for a GIF and an MP4, or `-o out/name.gif` for one format. The GIF path uses ffmpeg palettegen and paletteuse in a single split graph, with stats_mode diff and Bayer dithering, which suits flat chart graphics. Rendering uses Pillow only, so it works on machines where matplotlib is broken.
6. **QA the frames.** Run `bin/frame_qa.py plan.json out/name.gif --expect 1080x1080 --max-mb 8 --out-dir qa/` and the same for the MP4. Then look at the three extracted frames, first, middle, and last, and view the last frame shrunk to about 360 px wide. If values cannot be read at that size, fix the plan, not the render.
7. **Fix size problems in order.** Fewer frames, then smaller canvas, then fewer steps, then MP4 instead of GIF. The order and the reasoning are in `references/platform-specs.md`. Re-run QA after every change.

## Output Contract
- One GIF and/or one MP4 per plan, named after the piece, plus the plan JSON that produced them.
- GIF: loop forever, 12 fps by default, under 100 frames and under 6 seconds for GIPHY targets, under 8 MB unless the destination allows more.
- MP4: H.264, yuv420p, CRF 20, faststart, even dimensions.
- Every frame carries the title, the step heading, direct labels, values at the end of each bar, and the source line.
- A QA report from `bin/frame_qa.py` with all checks passing, and the three representative frames kept with the output for review.

## Operating Rules
1. Data accuracy comes before animation. No invented numbers, dates, or quotes in titles, notes, or values. Unverifiable means cut or clearly marked as illustrative.
2. Never encode a value by colour alone. Every bar gets a text label and a printed value, and red-green pairings at similar lightness are not used.
3. The first frame must work as a still image. Some email and feed clients show only the first frame, so it carries the full title, first step, and source.
4. Fix the plan, never the rendered frames. The plan JSON is the single source of truth, so a correction re-renders cleanly in both formats.
5. Platform numbers in `references/platform-specs.md` were checked on 2026-10-14 and are export targets, not guarantees. Verify the current limit at upload time for a launch that matters.
6. Contrast is checked, not guessed. `bin/frame_qa.py` computes the ratio from the plan colours and fails body text under 4.5 to 1, which is a rule of this skill.
7. Test before shipping. The sample plan must render and pass QA after any change to `bin/`. Commands verified on ffmpeg 8.1.2 and Pillow 10.2.0 on 2026-10-14: the sample rendered 56 frames in 4.67 seconds, 0.11 MB as a GIF and 0.062 MB as an MP4, all QA checks passing.
