Animated funnel chart in HTML (no D3, no After Effects)
Build an animated funnel chart in plain HTML and CSS — stacked trapezoids, conversion percentages that count up, deterministic MP4 export. No charting library required.
Someone on the growth team asks for an animated funnel chart by Thursday — visitors, signups, activations, paid — and the usual answer is a Figma frame, a screen recording, and a Slack apology when the numbers move on Friday. The funnel chart is the canonical marketing visualization, and it is almost always built in the wrong tool.
The right tool is HTML. A funnel is four trapezoids and four percentages. The animation is a width sweep and a counter. The data is a five-line JSON file. The output is an MP4 that re-renders the moment the numbers change.
What an animated funnel actually is
A funnel chart has three jobs and four parts:
- Tier blocks — one per stage, each narrower than the last. Conceptually trapezoids; practically, two stacked CSS shapes with
clip-pathor a single SVG<polygon>. - Stage labels — the name of the step ("Signups") and the absolute count ("18,420").
- Conversion percentages — the rate from one stage to the next, often called the "drop" or "step rate."
- A reveal — the tiers appear one at a time so the viewer reads top-to-bottom instead of all at once.
The reveal is what turns a static infographic into a video. Without it, you just have a screenshot of a Notion doc.
The data shape
A funnel is just an ordered array of stages. The percentages are derived — never stored, never hand-typed.
{
"title": "Acquisition funnel · Q2",
"stages": [
{ "label": "Visitors", "value": 124000 },
{ "label": "Signups", "value": 18420 },
{ "label": "Activated", "value": 7864 },
{ "label": "Paid", "value": 2287 }
]
}The template never sees absolute pixel widths. It computes everything from the data and the stage index. Swap visitors → signups → trials → MRR and the geometry adjusts itself.
The trapezoid trick
Most funnel-chart tutorials reach for <canvas> or SVG polygons. Both work. CSS works better, because the same primitive — clip-path: polygon(...) — can be styled, hovered, and themed with the rest of your design system.
Each tier is a rectangle clipped into a trapezoid. The top edge is full-width minus an inset; the bottom edge is full-width minus a larger inset. The inset grows with the stage index, which is what produces the funnel taper.
.shape {
position: absolute; inset: 0;
background: var(--accent, #ff3b1f);
clip-path: polygon(
var(--l) 0,
calc(100% - var(--l)) 0,
calc(100% - var(--lb)) 100%,
var(--lb) 100%
);
}The reveal is a horizontal scale on the shape, anchored to the center. As scaleX goes from 0 to 1, the trapezoid grows outward from a vertical sliver — readable, restrained, and impossible to mistake for a pop-in modal.
Counting the percentages
Two numbers ride along: the absolute count inside each tier, and the conversion rate between tiers. Both should count up over the same window as the tier reveal, with the same easing.
Avoid the temptation to interpolate linearly. A linear count next to a cubic-ease shape is the visual equivalent of two people clapping out of sync. The number is "at" 80% when the shape is at 80% — same eased t, same look.
For the conversion percentage, derive it. Never store it. stages[i+1].value / stages[i].value is one line and survives any data change without anyone having to remember to update both fields.
Parameterizing the funnel
The accent color, the headline, and the count of tiers are the three knobs anyone ever asks for. Wire them once and you can re-skin the funnel from any source — a CSV upload, a Notion query, a /funnel endpoint that takes a POST.
The speed knob is the one you'll reach for during a render review. Funnel videos almost always need to be 20% slower than they feel in dev — the count-ups are doing more work than the eye registers in the first pass.
Color and hierarchy
A funnel that is one solid color reads as one block. A funnel that is four different colors reads as four unrelated bars. The right call sits between: keep the top tier full-saturation, then step the opacity down 10–15% per tier. The viewer's eye follows the gradient downward, which is exactly the direction the funnel wants to be read.
If you must use distinct colors, reserve them for the conversion-rate labels (green for healthy, gray for typical, red for under target). Color the tiers themselves with restraint.
Render to MP4
Once the funnel renders correctly in the browser, the HyperFrames CLI takes the same HTML file and produces an MP4. The hf-seek event is the contract — your animation is a pure function of t, and the renderer dispatches t deterministically frame by frame.
hyperframes render funnel.html --out funnel.mp4 --duration 6 --fps 30Same JSON, same template, same MP4 — frame for frame. Re-run on Friday when the numbers change and the output is a different MP4 with no diff in the source. That is what deterministic rendering actually buys you: the build artifact is the video, not the After Effects project.
Variations worth knowing
A funnel chart in HTML opens up a few extensions that desktop tools make hard:
- Side-by-side cohorts. Two funnels in a grid, one per cohort, with shared y-axis values. Useful for A/B reads.
- Step deltas. Pin a small arrow + percentage to each tier, comparing to the previous period. Same easing, same reveal window.
- Stage filtering. Pass a
stagessubset and the template adapts — the trapezoid taper is computed from the index, not hardcoded.
The same template handles all three. The data is the variable, the chart is constant.
FAQ
Can I use this for a sales-pipeline funnel with custom stages?
Yes. The template is stage-agnostic — it reads the stages array and adapts. Pipeline funnels typically have 5–7 stages instead of 4. Adjust the per-tier inset so the bottom tier is at least 40% of the top width; below that, the label becomes unreadable.
How do I show conversion rates between non-adjacent stages?
Derive them in the template, not in the JSON. stages[3].value / stages[0].value is the overall conversion rate. A separate row beneath the funnel labeled "Overall: 1.8%" handles it; the reveal fires last, after all tiers have landed.
What if my funnel has wildly uneven stage sizes (e.g., 1M → 100 → 10)?
Two options. Use a log scale on the value (visual width = log(value) / log(max)) — accurate but harder to read. Or split into two funnels: a "discovery" funnel and a "conversion" funnel, each rescaled to its own max. The second is what real growth dashboards do.
Can I export a transparent-background WebM instead of an MP4?
Yes. The render pipeline supports both. Pass --format webm --transparent to the CLI. The background of the HTML becomes the alpha channel. MP4 doesn't support alpha; if you need transparency, you need WebM or ProRes 4444.
Does this work for a horizontal funnel (left-to-right instead of top-to-bottom)?
Yes, but reconsider. Horizontal funnels are harder to read because the eye scans left-to-right by default — the funnel taper competes with the reading direction. Reserve horizontal layouts for funnels embedded inside a wider dashboard, where vertical space is the constraint.
Where to go next
The funnel is the simplest case of "ordered stages with derived percentages." Once it works, the same primitive handles bar races, dropoff charts, and step-rate analysis. Drop the template into the playground and start swapping the JSON. The programmatic-video-from-data recipe shows how to wire it up to a database query so the funnel re-renders nightly without anyone opening a design tool.
Four trapezoids, three percentages, zero After Effects. Ship it.
Cite this postBibTeX · APA · Markdown
@misc{team2026animated,
author = {HyperFrames Team},
title = {Animated funnel chart in HTML (no D3, no After Effects)},
year = {2026},
url = {https://hyperframes.video/blog/animated-funnel-chart-html},
note = {HyperFrames blog}
}HyperFrames Team. (2026, May 21). Animated funnel chart in HTML (no D3, no After Effects). HyperFrames. https://hyperframes.video/blog/animated-funnel-chart-html
[Animated funnel chart in HTML (no D3, no After Effects)](https://hyperframes.video/blog/animated-funnel-chart-html) — HyperFrames Team, 2026
We build the deterministic HTML-to-video pipeline at HyperFrames. We write here when we have something concrete to say.
Animated line chart in HTML: 60 lines, no chart library
A clean animated line chart in plain HTML and SVG. Path tracing, gridlines, a moving dot, and a render-to-MP4 export. No D3, no Chart.js.
Build an animated countdown timer in 40 lines of HTML
A countdown timer with flip-card digits, target-date config, and a render-to-MP4 button. Pure HTML, no library.
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.
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.