Render an animated Gantt chart to MP4
An animated Gantt chart video built from a JSON of tasks. Horizontal bars that grow across a date axis with a moving today cursor — deterministic MP4 out.
The product team wants a one-minute Gantt chart video for the all-hands — six tracks, a quarter of work, a clear "we are here" cursor. The PM does it in Asana's screen-recorder. The recording is 14MB, jitters on the cursor, and is unusable two days later when the dates slip. The video is technically delivered and practically dead on arrival.
The fix is to treat the Gantt chart like every other piece of motion graphics: a function of a t value, rendered deterministically. Task bars are CSS divs with a transform: scaleX(). The today cursor is a vertical line that interpolates across the date axis. The data is a JSON of { name, start, end } rows. The output is an MP4 that re-renders the instant a date moves.
What an animated Gantt chart actually is
Four parts:
- A date axis — horizontal, usually a quarter (12 weeks) or a half (26 weeks). Tick marks for week or month boundaries.
- Task rows — one per task. Each row has a label on the left and a bar somewhere in the timeline.
- A reveal animation — bars grow from their start date toward their end date, staggered top-to-bottom so the eye reads each task before the next appears.
- A today cursor — a vertical line that sweeps across the timeline, signaling time elapsing. This is the part most static Gantt charts get wrong.
The cursor is the difference between "a Gantt screenshot" and "a Gantt video." Without it, you have a slide. With it, you have a story.
The data shape
A Gantt chart's natural data model is a flat list of tasks with start/end dates. The template converts dates to percentages of the timeline; the timeline's start and end are the only project-level knobs.
{
"title": "Roadmap · Q2 2026",
"start": "2026-04-01",
"end": "2026-06-30",
"tasks": [
{ "name": "Auth rewrite", "start": "2026-04-01", "end": "2026-04-28", "track": "platform" },
{ "name": "Billing v2", "start": "2026-04-08", "end": "2026-05-15", "track": "growth" },
{ "name": "API limits", "start": "2026-04-22", "end": "2026-05-21", "track": "platform" },
{ "name": "Dashboard", "start": "2026-04-28", "end": "2026-06-12", "track": "design" },
{ "name": "Mobile beta", "start": "2026-05-15", "end": "2026-06-23", "track": "growth" },
{ "name": "Docs refresh", "start": "2026-05-29", "end": "2026-06-30", "track": "design" }
]
}The pct() helper is the entire trick. Dates become percentages; percentages become CSS left and width. The template never knows the difference between April and a date that hasn't been chosen yet.
The bar reveal
Each task bar starts at scaleX(0), anchored to its left edge (the task's start date), and animates to scaleX(1) over about one second. A cubic ease-out lands the right edge without ringing past it.
The stagger between rows is the part most tutorials get wrong. 80ms is too tight — the bars look like they're firing in a single block. 300ms is too loose — the video drags and the cursor catches the last bar mid-grow. 150–200ms per row is the right window for a six-bar chart over six seconds.
The today cursor
The cursor is a vertical line at left: X% where X is easeInOut(t) mapped onto the date span. It enters after the last bar has fired, sweeps left-to-right, and lingers at the right edge for the final beat.
A small block at the top of the cursor — 12×12 pixels, same color as the line — gives the eye something to track. Without that block, the cursor reads as a divider, not a playhead. With it, the eye locks on.
For a real Gantt chart, the cursor's position can be derived from "today" (i.e., the date the video is rendered), not from t. That turns the video into a status update: every nightly render shows the cursor a little further along, and the stale tasks behind it look stale on purpose.
Color by track, not by task
Six different colors on six different bars is noise. Three colors on three tracks ("platform" green, "growth" orange, "design" blue) is signal. The viewer learns the color key in the first two seconds and reads the chart by team for the remaining four.
Keep accents to brand colors. Ink #0a0a0a for the cursor. Cream #f6f5f1 for the background. Signal-orange #ff3b1f for the highest-priority track. The greens and blues are secondary — desaturate them slightly so the orange stays dominant.
Parameterizing the chart
The two knobs that come up most often in a render review: the bar stagger (how fast the rows fire) and the cursor sweep duration. Wire both as variables and the same template handles a six-week sprint or a year-long roadmap.
Render to MP4
The HTML template is the source of truth; the renderer turns it into a video. With hf-seek driving every property — bar scale, cursor position, opacity — the output is byte-identical across machines.
hyperframes render gantt.html --out roadmap.mp4 --duration 6 --fps 30For longer roadmaps (a 60-second status report), bump --duration 60 and the same template stretches. The stagger and sweep variables let you keep the pacing readable at any length. Compare against deterministic rendering for CI for the wider pipeline picture.
Where this falls down
A few cases where you reach for a real project-management tool:
- Dependencies and critical path. Drawing arrows between dependent tasks is possible (SVG overlay), but quickly becomes the chart's dominant feature. If dependencies are the story, render the dependency graph instead — see the org chart post for the tree-drawing pattern.
- Resource allocation overlays. "Who is on what" is a different chart. Render it as a separate stacked bar below the Gantt.
- Live editing. This is a video tool, not a planning tool. Edit the JSON in your PM tool of record; the chart is a build artifact.
FAQ
How do I show overdue tasks?
Compute today > end per task and apply a red outline or a hatched fill. The reveal still fires the same way; the styling is conditional. For a status video, overdue tasks read better with a static red bar and a small "+8d" label than with an animated state change.
Can I render this at 4K for a TV?
Yes. Set the HTML body to 1920x1080 or 3840x2160 and pass --width 3840 --height 2160 to the CLI. The CSS percentages adapt automatically; the only thing to revisit is the font sizes — 22px at 1080p is 44px at 4K.
What about tasks that span more than the visible timeline?
Clamp the bar's left and width to 0% and 100%, and add a small chevron at the clipped edge. The chevron tells the viewer "this task extends beyond the frame," which is enough context for a status video.
How do I generate the Gantt from a Linear/Jira/Asana export?
Map their JSON to the { name, start, end, track } shape with a 20-line script. Linear and Jira both expose startDate/dueDate fields; the only edge case is in-flight tasks with no start date, which you set to "today" before rendering. See programmatic video from data for the wiring pattern.
Can the cursor pause on a specific date (e.g., the launch)?
Yes. Split the cursor's t-curve into segments: 0–60% of the sweep over the first 3 seconds, hold at 60% for one second, then 60–100% over the last second. It's a piecewise function of t — the easing is yours to design.
Where to go next
A Gantt chart is the date-axis special case of "events over time." The same primitives — horizontal bars, a moving cursor, an axis — handle release calendars, sprint timelines, and historical timelines. Drop the JSON into the playground and try a different date range; the chart adjusts itself. For the broader recipe of building data-driven videos like this, see programmatic video from data.
Six bars, one cursor, zero PM tool screenshots.
Cite this postBibTeX · APA · Markdown
@misc{tanaka2026render,
author = {Kira Tanaka},
title = {Render an animated Gantt chart to MP4},
year = {2026},
url = {https://hyperframes.video/blog/animated-gantt-chart-video},
note = {HyperFrames blog}
}Kira Tanaka. (2026, May 21). Render an animated Gantt chart to MP4. HyperFrames. https://hyperframes.video/blog/animated-gantt-chart-video
[Render an animated Gantt chart to MP4](https://hyperframes.video/blog/animated-gantt-chart-video) — Kira Tanaka, 2026
Kira works on the render core: headless Chromium scheduling, frame capture, and the encoder pipeline. She cares about reproducible builds and small numbers next to the word "variance."
Animated timeline infographic generator
Generate a timeline infographic video from a JSON of milestones — a vertical spine draws downward, dots land on dates, labels slide in from alternate sides. Deterministic MP4.
Animated poll results as MP4
Build a poll results animation that renders to deterministic MP4: four horizontal bars race to their final percentages, then a winner ribbon sweeps in.
Animated recipe card videos for social
Build a recipe card video for Instagram, TikTok, and Pinterest — ingredients check off line-by-line, a step counter ticks, and a circular timer fills. Rendered deterministically to MP4.
Building with HyperFrames? Come hang out.
We're on GitHub, in Discord, and the playground is one click away. Bring weird ideas — we collect them.