Skip to content
Back to skills

Ae Data Driven

ASecurity

Drive After Effects from data through the AE MCP Bridge - import CSV, TSV or JSON as footage and read it in expressions (dataValue, sourceData), build animated bar charts and counters from a spreadsheet, or have the agent read the data and generate one version per row with batch tool calls. Verified in After Effects. Use when asked to animate data, make a chart or infographic, build versions from a spreadsheet or JSON (names, prices, scores, cities), or keep a comp updated when numbers change.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 19, 2026
ai-agentsexpress

Works with

  • cli
  • mcp

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add solomondivyananth/aftereffects-mcp --skill ae-data-driven --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ae Data Driven?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ae Data Driven
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/solomondivyananth-ae-data-driven/badge)](https://www.skillsdirectory.com/skills/solomondivyananth-ae-data-driven)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ae-data-driven
description: Drive After Effects from data through the AE MCP Bridge - import CSV, TSV or JSON as footage and read it in expressions (dataValue, sourceData), build animated bar charts and counters from a spreadsheet, or have the agent read the data and generate one version per row with batch tool calls. Verified in After Effects. Use when asked to animate data, make a chart or infographic, build versions from a spreadsheet or JSON (names, prices, scores, cities), or keep a comp updated when numbers change.
---

# Data-driven animation

There are two ways to drive a comp from data. Choose by whether the data
should stay **live**:

| Approach | When |
|---|---|
| **A. Data as footage + expressions.** The file is in the project and expressions read it. | The numbers will change and the comp should update. One comp shows many values (charts). |
| **B. The agent reads the data and builds with tool calls.** | One output per row (versions, cards, social tiles), or the data needs cleaning first. |

Whichever you pick, **look at the data before you build anything**: row
count, the longest string, the biggest and smallest number, empty cells. Two
minutes there saves rebuilding forty comps.

## Clean data first

After Effects is unforgiving about file details, and every one of these
fails quietly rather than loudly:

| Trap | What it does | Fix |
|---|---|---|
| A byte-order mark or odd encoding | the first header is unreadable, `dataValue` returns nothing | save as plain UTF-8 |
| Semicolons as separators (European exports) | the whole row is one column | save as comma-separated, or use `.tsv` |
| Decimal commas (`3,5`) | `Number()` gives NaN | convert to points before importing |
| Thousands separators (`1,200`) | splits the column, or NaN | strip them; format for display in the expression instead |
| Spaces in headers or values | lookups by name miss | trim them |
| Mixed types in a column | some rows animate, some do not | decide the type per column and enforce it |
| A trailing empty line | one blank extra row | remove it, or check the row count |

## A. Data files as footage

`ae_import_file` a `.csv`, `.tsv` or `.json`. It becomes a footage item named
after the file. Expressions read it with `footage("<file name>")`.

**JSON:** `sourceData` is the parsed object.

```js
var d = footage("data.json").sourceData;
d.title + ": " + d.items[0].label + " " + d.items[0].value
```

**CSV / TSV:** use `dataValue([column, row])`. Both count from 0, and the
header row is **not** counted, so `[0, 0]` is the first data row's first
column. Values come back as text: wrap numbers in `Number()`. `sourceData` is
empty for CSV, so don't use it.

```js
footage("data.csv").dataValue([1, 2])
```

(Third data row, second column.)

### Bar chart recipe

For each row, one shape bar grows from a baseline to its value:

```json
ae_shape {"name": "Bar 1", "shape": "rect", "size": [80, 400], "position": [0, -200], "fill": "#7a5cff"}
ae_set_property {"layer": "Bar 1", "path": ["Transform","Position"], "value": [400, 900]}
```

The rectangle sits above the layer's origin (position `[0, -200]` for a
400-tall bar), so scaling Y grows it up from the baseline at y 900. Scale
from the data, with a staggered ease-in (row *r* starts 0.1 s later):

```js
var v = Number(footage("data.csv").dataValue([1, 0]));
var grow = ease(time, 0.2, 1.0, 0, 1);
[100, v * grow]
```

Here the value is used as a percentage of the bar's full height. For other
ranges, normalise it first: `v / maxValue * 100`.

Colour from a hex column, on
`["Contents","Bar 1","Contents","Fill 1","Color"]`:

```js
hexToRgb(footage("data.csv").dataValue([2, 0]))
```

Label and value text use Source Text expressions. Counters that roll up use
`Math.round(linear(time, t0, t1, 0, value))` (see `ae-expressions`).

Build all the bars in one `ae_batch`, one set of calls per row, changing only
the row index and the x position.

### Beyond bars

| Chart | How to build it |
|---|---|
| Stacked bars | one bar layer per series, each offset by the sum of the ones below — compute the offsets yourself and write them as values |
| Donut or progress ring | a shape ellipse with a stroke and Trim Paths; End = `value / max * 100` |
| Line chart | build the path from the data yourself and write it with `ae_shape` as a `path`, then reveal it with Trim Paths. Expressions cannot make a path out of a CSV; arithmetic in your own head can |
| Scatter | one small shape per row, positioned from two columns, staggered in |
| Table or leaderboard | one row precomp, duplicated per row, each with its own text; sort the rows yourself before building |
| A counter | `Math.round(linear(time, t0, t1, 0, value))` on Source Text |

**Scale everything to one maximum.** Find the biggest value across the rows
yourself and write it into the build (or into a Slider on the Controls
null), so all the bars share one scale. A chart where each bar is scaled to
its own maximum is a lie.

**Round for reading, not for maths.** Show whole numbers, or one decimal;
put the unit in the label, not on every bar.

**Number formatting** matters more than the animation: pad counters to a
fixed width and use tabular figures, or the text reflows every frame and
shimmers (`ae-typography`).

### Live or baked

Expressions re-evaluate every frame, on every layer. Forty bars each reading
a CSV is slow to preview and slow to render.

- Keep it live while the numbers are still changing.
- Once they are final, bake it: `ae_menu_command {"command": "Convert
  Expression to Keyframes"}` with the property selected (Animation ▸
  Keyframe Assistant). If the command name differs in this version the tool
  says so — look it up rather than guessing.
- Or skip expressions entirely and have the agent write plain keyframes from
  the data in one `ae_batch`. For a one-off chart that is both faster to
  build and faster to render.

### Updating

Replace the data file on disk and use `ae_item_action {"action": "reload",
"item": "data.csv"}`. Every expression picks up the new numbers. To point at
a different file, use `ae_item_action {"action": "replace_source", …}`.

## B. One version per row

When each row is its own deliverable (a name card per speaker, a price tile
per product):

1. Read the file yourself and check it. Trim spaces, flag empty cells and
   over-long text, and confirm the row count with the user before building 40
   comps.
2. Build **one master comp** whose changeable parts are on a Controls null
   and in named text layers (see `ae-templates`).
3. For each row, in one `ae_batch` per row:
   `ae_duplicate_comp` (name it from the row, e.g. `Card - Oslo`), then
   `ae_text` on each text layer, then `ae_set_property` for the Controls
   (colours, switches).
4. Check a few versions, especially the longest text: `ae_render_frame`.
5. Queue them: `ae_render_queue {"action": "add", "comp": …, "output": …}` per
   version, then render or send to Media Encoder (`ae-deliver`). Name the
   outputs from the row.

## Pitfalls

| Symptom | Cause |
|---|---|
| Value is off by one row | the CSV header row isn't counted, so row 0 is the first data row |
| Bars don't scale, the expression error mentions NaN | the CSV value is text: wrap it in `Number()` |
| `sourceData` is null | it's a CSV: use `dataValue` |
| Expression can't find the footage | the name must match the Project panel item exactly, extension included (check with `ae_list_items`) |
| Bars grow from the middle | the shape isn't offset above the layer's origin: set the rect's position to minus half its height |
| Numbers jitter as they count | proportional figures reflow every frame | pad the string and use a tabular font |
| Everything previews slowly | dozens of per-frame expressions reading a file | bake to keyframes, or build the values in |
| A reloaded file changes nothing | the item was replaced on disk but not reloaded | `ae_item_action {"action": "reload"}` |

## Check it

Render the chart at its final state and read the bars and labels back
against the data (`ae_render_frame`) — literally compare each number on
screen with the source row. A chart that animates beautifully and shows the
wrong figure is the worst outcome in this skill.

Then `ae_review_motion` over the build to check the stagger, and `ae_proof`
for the mechanical faults data tends to cause: labels overlapping their bars
when a value is small, text clipped by the frame when a name is long,
uneven stagger across generated rows (`ae-motion-proof`).

Before delivering versions, spot-check the first row, the last row, the
longest text and the largest and smallest values.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.