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.
An animated boarding pass video is a flight card where a plane icon arcs across a horizon line between two airports, the gate and departure-time fields settle into place, and the whole pass folds out from a single edge. It's the asset airlines and booking platforms send as a check-in reminder, and it works because the animation is the metaphor — the plane moving is the point.
The polished version below renders in 6 seconds at 30fps. All motion is driven by hf-seek and the SVG arc trajectory is computed from a single curve.
The four-act structure
A boarding pass animation has four distinct beats. Get the timing right and it reads as one motion; get it wrong and it feels like four motions glued together.
- Fold-out (0 - 0.7s). The card rotates from edge-on to face-on, like opening a wallet.
- Hold (0.7 - 1.0s). A 300ms beat where nothing moves. The eye reads the airports.
- Plane arc (1.0 - 3.6s). The plane traces a quadratic curve from origin to destination, rotated to face its direction of travel.
- Field settle (3.8 - 4.8s). Gate, time, seat, class fade up with a small Y offset, staggered by 120ms each.
Total active animation: 4.8 seconds. Hold the final frame for 1.2 seconds. Six seconds total, with the visual hook landing in the first two.
The fold-out
The card starts at rotateY(-90deg) — edge-on, invisible from the front. The transform-origin: left center makes it pivot from its left edge, which gives the illusion of opening from a wallet or seat-back pocket. The fold completes in 700ms with cubic ease-out.
.pass {
transform-origin: left center;
transform: perspective(900px) rotateY(-90deg);
opacity: 0;
}const fp = Math.min(1, t / 0.7);
const fe = 1 - Math.pow(1 - fp, 3);
pass.style.transform = `perspective(900px) rotateY(${-90 + 90 * fe}deg)`;
pass.style.opacity = fe;The perspective(900px) is what gives the rotation depth. Without it, the card scales horizontally instead of rotating in 3D space. Keep perspective at 800-1200px for most card sizes — lower values are too dramatic, higher values flatten back into a 2D scale.
The arcing plane
The arc is a single SVG quadratic Bezier: M5,40 Q100,-10 195,40. Two endpoints near the bottom, a control point above the card — the result is a smooth arc that peaks in the middle, mimicking the way flight maps render great-circle routes.
The plane position is sampled with getPointAtLength:
const ap = Math.max(0, Math.min(1, (e.detail.time - 1.0) / 2.6));
const ae = ap < 0.5 ? 2*ap*ap : 1 - Math.pow(-2*ap+2, 2)/2; // ease in-out
const pt = trackPath.getPointAtLength(len * ae);
const next = trackPath.getPointAtLength(Math.min(len, len * ae + 1));
const angle = Math.atan2(next.y - pt.y, next.x - pt.x) * 180 / Math.PI;
plane.setAttribute('transform', `translate(${pt.x} ${pt.y}) rotate(${angle})`);The easing here is quadratic in-out, not the cubic out used elsewhere. A plane accelerates from a standstill and decelerates to a stop — that's an in-out curve, not an out-only. If you use cubic-out here the plane "appears at speed" at the origin, which reads wrong because it implies the journey started earlier.
The dotted track behind the arc is the same path with stroke-dasharray: 3 4 and a lighter stroke. It's drawn at full length from the start. The colored arc draws over the dots as the plane moves, which makes the dots the "remaining distance" indicator.
Staggered field reveal
Four pieces of metadata — gate, boards, seat, class — fade up with a 120ms gap between each. The reveal happens after the plane has landed, so the eye is already done watching motion and is ready to read.
fields.forEach((el, i) => {
const ts = 3.8 + i * 0.12;
const p = Math.max(0, Math.min(1, (t - ts) / 0.5));
const ease = 1 - Math.pow(1 - p, 3);
el.style.opacity = ease;
el.style.transform = `translateY(${6 - 6 * ease}px)`;
});The 6px Y offset is deliberately small. A boarding pass field is a data point, not a hero element — it should settle subtly. Larger offsets (20px+) make the fields look like they're emerging from somewhere; small offsets make them feel like they're focusing into place.
Data shape
For a per-passenger pipeline (check-in reminder emails, app push), the template takes flight + passenger fields and renders one MP4 per booking.
{
"airline": "HF AIRWAYS",
"flight_number": "HF 422",
"from": { "iata": "SFO", "city": "San Francisco" },
"to": { "iata": "JFK", "city": "New York" },
"gate": "A12",
"boards_at": "07:45",
"seat": "14A",
"class": "Main",
"accent": "#ff3b1f"
}This is a programmatic video from data pipeline. The arc path is fixed in the SVG — it doesn't need to reflect geography. The metaphor "two cities, a curve between them" carries the meaning; geographic accuracy would mean recomputing the SVG path per route, which adds complexity without adding readability at card size.
Render to MP4
hyperframes render pass.html --out clip.mp4 --duration 6 --fps 30For email-attachable clips, drop to 24fps and --width 720 --height 540 — a 4:3 card crops cleanly into most inbox previews. For social, use --width 1080 --height 1080 square. The card centers via flexbox, so the change is purely a viewport setting. See the quickstart for the full CLI surface, and deterministic rendering for why every render of the same HTML produces a byte-identical MP4.
FAQ
Can I show layovers?
Yes — extend the SVG with two arcs and two destination IATAs. The arc-tracing logic generalizes: concatenate the two paths into one path string, take the total length, and the plane traces both segments continuously. The control points for each arc determine the layover "shape."
Should the plane icon be a real plane silhouette?
The simple triangle (M-8,-4 L8,0 L-8,4 L-4,0 Z) works at recap size and renders crisply at any resolution. A detailed plane silhouette becomes a smudge below 16px. If you want more character, use a slightly-elaborated triangle (add wings, keep the body) but stay vector — raster icons pixelate when the plane rotates.
How do I localize the labels?
The labels (Gate, Boards, Seat, Class) are part of the template, not the data. For multi-locale renders, pass a locale string and have the template select from a label dictionary. Keep the airport codes in IATA — they're internationally consistent and don't need translation.
Why doesn't this use a real map background?
Map tiles introduce two problems: network requests during render (slow, non-deterministic) and visual noise that competes with the route arc. The blank card forces the eye onto the arc, which is the entire metaphor. If you need a map for premium fare visualization, render the map tile to a static image server-side and embed it as a base64 data URI.
Can I animate the boarding pass tear?
Yes — add a third element after the field settle: the right edge of the pass "tears" along the existing dashed border. Implement it with a clip-path that retreats from 100% to ~70% of the card width over 400ms. It's a nice touch for check-in confirmations specifically, but skip it for reminder clips where the action hasn't happened yet.
Related
- Animated route map video — full-screen route reveals with map context
- Animated invoice summary video — same card-fold-out pattern, different content
- Easing curves cheatsheet — why ease-in-out beats ease-out for the plane arc
Cite this postBibTeX · APA · Markdown
@misc{tanaka2026animated,
author = {Kira Tanaka},
title = {Animated flight itinerary cards in HTML},
year = {2026},
url = {https://hyperframes.video/blog/animated-boarding-pass-card},
note = {HyperFrames blog}
}Kira Tanaka. (2026, May 21). Animated flight itinerary cards in HTML. HyperFrames. https://hyperframes.video/blog/animated-boarding-pass-card
[Animated flight itinerary cards in HTML](https://hyperframes.video/blog/animated-boarding-pass-card) — 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 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.
Turn any SVG animation into a real MP4
SMIL, CSS, JS-driven — every flavor of SVG animation, rendered to a deterministic MP4 you can ship anywhere.
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.
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.