Animated job listing videos for LinkedIn
Build a job listing video for LinkedIn: company logo lands, title fades up, requirements stagger in, and an Apply button pulses — exported as deterministic MP4.
A job listing video is the LinkedIn-native format that turns a static posting into a 6-second clip: the company logo lands, the role title fades up, requirements stagger in one-by-one, and a pulsing "Apply" button parks at the bottom. Done well, it doubles the click-through over a plain-text post because the requirements appear in sequence rather than as a wall of bullets, which is how the eye prefers to consume a list.
The polished version below runs entirely off hf-seek. Five requirements, five distinct fade-up beats, a final pulse.
The recruiting case for video
Static job posts have a fundamental UX problem on social: they ask the reader to scan and decide in one motion. Video breaks the decision into a sequence — see the logo, read the title, parse the requirements one at a time, end on a clear action. The eye does not need to scan because the post scans itself.
LinkedIn's own data shows video posts get 3-5x the engagement of text posts in the recruiting category. A 6-second auto-looping clip costs the same to produce as a polished image (less, with a templating pipeline) and outperforms in feed.
The beat sheet
Six seconds, eight beats:
| Time | Beat | Duration |
|---|---|---|
| 0.1s | Logo drops in | 500ms |
| 0.5s | Title fades up | 500ms |
| 0.7s | Company line fades up | 500ms |
| 1.0s | "Requirements" label appears | 400ms |
| 1.2-3.0s | Bullets stagger (180ms gap × 5) | 1.8s |
| 3.2s | Apply button enters | 500ms |
| 4.0s | Pulse begins | infinite |
| 6.0s | Loop point | — |
The pulse continuing past the loop is intentional — when the MP4 autoplays again, the pulse picks back up immediately from the start, which feels like continuous motion across the loop boundary.
Driving every beat from a single seek listener
The whole animation is a function of t. There's one helper, fade(el, t, start, dur, offset), and every element calls it with different parameters.
function fade(el, t, start, dur, offset) {
const p = Math.max(0, Math.min(1, (t - start) / dur));
const e = 1 - Math.pow(1 - p, 3);
el.style.opacity = e;
el.style.transform = `translateY(${offset - offset * e}px)`;
}
addEventListener('hf-seek', (event) => {
const t = event.detail.time;
fade(logo, t, 0.1, 0.5, -12); // drops from above
fade(title, t, 0.5, 0.5, 8); // rises from below
fade(company, t, 0.7, 0.5, 8);
bullets.forEach((li, i) => fade(li, t, 1.2 + i * 0.18, 0.5, 8));
});This is the pattern for every staggered reveal in HyperFrames. The seek listener does not track state — it computes the visual state from scratch every frame. Seek to any t in any order and you get a deterministic result. That's what deterministic rendering buys you.
The pulse
The Apply button pulses with Math.sin((t - 4.0) * Math.PI * 2) * 0.025 + 1 — a 1Hz sinusoid producing scale values between 0.975 and 1.025. That's a 5% peak-to-peak amplitude, just enough to read as "alive" without being distracting.
if (t > 4.0) {
const pulse = Math.sin((t - 4.0) * Math.PI * 2) * 0.025 + 1;
btn.style.transform = `scale(${pulse})`;
}Two mistakes to avoid:
- Don't pulse the opacity. A fading-in-and-out button reads as "loading," not "ready." Pulse scale only.
- Don't go above 1.05. A 10% bump is the threshold where pulse becomes thump. You're nudging the eye, not slapping it.
Customize for your role
Data shape for batch rendering
For a recruiting team posting 20 roles a week, the template takes one JSON object per role and emits one MP4. The pipeline scales linearly with role count, and the output filenames map to job IDs for ATS integration.
{
"id": "JOB-422",
"title": "Senior Frontend Engineer",
"company": "Hyperframes",
"location": "Remote",
"brand_color": "#2b66ff",
"logo_initial": "H",
"requirements": [
"5+ years building production React apps",
"Strong CSS and animation fundamentals",
"Experience with Next.js App Router",
"Comfortable with TypeScript strict mode",
"Care for craft over surface area"
],
"apply_url": "https://jobs.hyperframes.dev/JOB-422"
}The render command stays identical across all roles — only the input HTML (templated from the JSON) changes. This is the programmatic video from data recipe applied to recruiting.
Render to MP4
hyperframes render job.html --out clip.mp4 --duration 6 --fps 30For LinkedIn feed, the optimal size is 1080×1080 square. The card already centers, so pass --width 1080 --height 1080 and tweak the card's max-width if you want more whitespace. For LinkedIn Stories or vertical-only feeds, use 1080×1920. See the quickstart for installation, and the playground for iterating on the HTML interactively.
FAQ
Does this work for hourly/contractor roles?
Yes — replace "Requirements" with "What we need" or "Shifts available" and the bullet pattern adapts to any list. For shift-based roles, consider replacing the bullets with day-of-week chips (a row of "M T W T F" with available days highlighted) — same stagger logic, different visual grammar.
Can I include the salary range?
Put it in the company line or as a sixth element between the title and the requirements label. Salary is a strong filter for the viewer — show it early so people who don't qualify scroll past, and people who do are sold harder by the requirements. Hiding salary until the end is a tax on everyone's time.
What about Open Graph cards on LinkedIn?
For LinkedIn-shared links, the OG card is a static image, not the video. Render a still frame from the video (typically t=3.5s, when all requirements are visible) and use that as the OG image. See animated open graph images for the full pattern.
How do I A/B test different versions?
Generate two MP4s from the same template with different copy or colors. Post both to your test audiences, measure click-through to the apply URL. Because the renders are byte-deterministic for identical inputs, you can confidently attribute differences to the input variables — there's no rendering jitter to control for.
Can the bullets animate in a different order than top-to-bottom?
You can, but you shouldn't. The visual contract is "list reveals top-down" — that's how Western reading conventions work, and breaking it (right-to-left, alternating, etc.) creates more friction than novelty. Save creative ordering for the title typography, not the bullet sequence.
Related
- Animated quote cards for Twitter — same fade-up pattern, different content
- Animated KPI cards — count-up technique you can adapt for "120+ applicants"
- Batch personalized videos from CSV — pipeline for one MP4 per row
Cite this postBibTeX · APA · Markdown
@misc{okafor2026animated,
author = {Marcus Okafor},
title = {Animated job listing videos for LinkedIn},
year = {2026},
url = {https://hyperframes.video/blog/animated-job-listing-video},
note = {HyperFrames blog}
}Marcus Okafor. (2026, May 21). Animated job listing videos for LinkedIn. HyperFrames. https://hyperframes.video/blog/animated-job-listing-video
[Animated job listing videos for LinkedIn](https://hyperframes.video/blog/animated-job-listing-video) — Marcus Okafor, 2026
Marcus leads design and motion at HyperFrames. Before that he shipped editorial motion for newsrooms and product launches. He thinks every easing curve has a personality.
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.
Podcast audiograms with animated waveforms
A podcast audiogram generator built in HTML: a 32-bar waveform animates with per-bar phase offsets, captions appear in sync, and the episode title sits on top.
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.