Animated coupon and discount banners as MP4
A discount banner animation that counts up the percentage, wiggles its dashed edge, pulses urgency copy, and shimmer-strikes the original price. Built from HTML, rendered to MP4.
A coupon banner has one job: make a discount feel like a moment. A static "30% OFF" graphic does not. A six-second MP4 with a counting percentage, a wiggling perforated edge, and a price strikethrough that catches light — that does.
Below is the full pattern. Drop a SKU's old and new price into the JSON, render one MP4 per product, and dispatch via your ad pipeline.
Four motions, layered
A coupon is busy by nature — discount, price, urgency. The animation has to give each element its own beat.
- Percentage count-up in the first 1.6s. Cubic-out easing, tabular numerals so the digits don't jitter.
- Dashed edge wiggle runs the whole 6 seconds. Two sine waves at different frequencies — feels like a real piece of paper twitching in a draft.
- Urgency badge pulse starts after the count-up lands (1.6s). Sine-driven
scalebetween 0.95 and 1.05. - Price strikethrough + shimmer finishes the sequence (2.4–4.4s). The line draws left-to-right, then a white gradient sweeps across the crossed-out price.
The dashed-edge wiggle
A 2px dashed border inset 6px from the coupon edge. Per-frame, rotate by a few tenths of a degree and translate by a sub-pixel. The eye reads constant micro-motion as "this is alive":
const wiggle = Math.sin(t * 18) * 0.4 + Math.sin(t * 11) * 0.25;
df.style.transform = `rotate(${wiggle}deg) translateY(${Math.sin(t*9)*0.6}px)`;Two sine waves at non-integer ratios (18 and 11) give the wiggle a non-repeating feel. A single sine looks robotic; two stack into something organic.
The strikethrough
The old price gets a ::after pseudo-element styled as a 3px bar across the middle, scaled from scaleX(0) to scaleX(1) via a CSS custom property:
.old::after {
content: '';
position: absolute;
left: -4px; right: -4px;
top: 50%;
height: 3px;
background: #ff3b1f;
transform: scaleX(var(--sx, 0));
transform-origin: left center;
}The hf-seek handler updates --sx per frame. CSS variables are first-class state for deterministic motion: cheap, scoped, and they don't trigger layout. After the bar draws across, a separate <span class="shimmer"> overlay sweeps a white gradient over the crossed-out text — half a second of "this discount is real."
Tweak the offer
Per-product, at scale
{
"sku": "WND-204",
"discountPct": 30,
"oldPrice": "$148.00",
"newPrice": "$103.60",
"urgency": "ends Sunday",
"label": "winter clearance"
}We've watched ad teams ship one MP4 per SKU across 2,000 product catalogs from this pattern. Render time per clip is ~10 seconds on a single CI core, so 2,000 clips finishes overnight on a modest runner. See batch personalized videos and programmatic video from data.
Render to MP4
hyperframes render input.html --out clip.mp4 --duration 6 --fps 30For Meta and TikTok ads, render square (--width 1080 --height 1080) or vertical (--width 1080 --height 1920). For Google Display banners that accept video, render at the slot dimensions. The CSS scales fluidly; the hf-seek timing is resolution-independent.
Iterate the spring constants and color choices in the playground before locking the render — once the curves feel right, they apply to every SKU you batch.
FAQ
How long should a coupon banner video be?
Six seconds is the upper bound for ad placements. Three to four seconds works for in-feed display. The animation above can be shortened by scaling all hf-seek thresholds proportionally — the same easing curves still read correctly at 3s.
Can I show a code (like SAVE30) instead of a percentage?
Yes. Replace the giant % element with a styled code block (SAVE30) and animate it via a letter-by-letter fade-in instead of a numeric count-up. The other beats — wiggle, pulse, strikethrough — stay identical.
How do I match my brand instead of red?
Change the accent color in CSS. The signal red (#ff3b1f) reads as discount in our default; swap it for any saturated brand color. Keep the new price green (#1f8a5b) — green for savings is a strong cross-cultural cue.
What if my prices have many digits, like $1,248.99?
Add font-variant-numeric: tabular-nums to .old and .new. The strikethrough length depends on the rendered width, so the animation will adapt automatically. For four-or-more-digit prices, reduce the price font-size to keep the layout balanced.
Can the coupon respect a real expiry date?
You can render different MP4s per region/timezone using build-time data. The video itself can't know "now" — that's antithetical to determinism. Instead, render a fresh MP4 every hour with the current Date.now() + 24h baked in. See deterministic rendering.
Related: animated countdown timer in HTML, animated pricing table video.
Cite this postBibTeX · APA · Markdown
@misc{team2026animated,
author = {HyperFrames Team},
title = {Animated coupon and discount banners as MP4},
year = {2026},
url = {https://hyperframes.video/blog/animated-coupon-banner-generator},
note = {HyperFrames blog}
}HyperFrames Team. (2026, May 21). Animated coupon and discount banners as MP4. HyperFrames. https://hyperframes.video/blog/animated-coupon-banner-generator
[Animated coupon and discount banners as MP4](https://hyperframes.video/blog/animated-coupon-banner-generator) — HyperFrames Team, 2026
We build the deterministic HTML-to-video pipeline at HyperFrames. We write here when we have something concrete to say.
Animated flight itinerary cards in HTML
Build an animated boarding pass video: a plane traces an arc from origin to destination, gate and time settle in, the pass folds out — rendered as MP4.
Animated fitness summary cards (Strava-style recaps)
Build a fitness recap video generator: distance, pace, and elevation tick up, a route map traces itself, and weekly streak dots fill in — exported as 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.
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.