# Landing Page Hero Video: Format, File Size, Autoplay and Poster Frame


A good landing page hero video is a **10 to 20 second muted loop**, encoded as **WebM (VP9) with an MP4 (H.264) fallback**, with **no audio track**, a **small poster image** that shows the first frame, and a **pause button**. Put it in `<video autoplay muted loop playsinline poster="…">` and it will autoplay in Chrome, Safari, Firefox and on iPhones. Don't use a GIF: in our test the same clip was about 57 times larger as a GIF than as a WebM.

Here's the full setup, with the reasons and the sources behind each choice.

<figure>
<video src="https://lumora.video/blog-media/landing-page-hero-video/hero.mp4" poster="https://lumora.video/blog-media/landing-page-hero-video/hero-poster.jpg" width="960" height="540" muted loop playsinline controls preload="none" data-autoplay></video>
<figcaption>The test clip: a 7.5-second hero loop of a sample CRM, made in Lumora. This is the 354 KB MP4 from the table below.</figcaption>
</figure>

## The hero video HTML you can copy

```html
<video
  class="hero-video"
  autoplay muted loop playsinline
  preload="metadata"
  poster="/hero-poster.jpg"
  width="1920" height="1080"
  aria-label="Product demo: creating an invoice and getting paid">
  <source src="/hero.webm" type="video/webm">
  <source src="/hero.mp4" type="video/mp4">
</video>
<button class="hero-pause" aria-pressed="false">Pause</button>
```

What each part does:

| Attribute | Why it's there | Source |
|---|---|---|
| `autoplay muted` | Chrome: "Muted autoplay is always allowed." Unmuted autoplay depends on the user's history with your site | [Chrome for Developers](https://developer.chrome.com/blog/autoplay) |
| `playsinline` | On iPhone, plays inside the page instead of going full screen | [WebKit](https://webkit.org/blog/6784/) |
| `loop` | Replays from the start; design the last frame to match the first | — |
| `poster` | The image shown "while the video is downloading"; without it, nothing shows until the first frame arrives | [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/video) |
| `preload="metadata"` | A hint, not a command; the spec advises `metadata` as the default | [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/video) |
| `width` / `height` | Reserves the space so the layout doesn't jump when the video loads | — |
| WebM first, MP4 second | Browsers use the first source they can play | [web.dev](https://web.dev/articles/replace-gifs-with-videos) |

## Will it autoplay everywhere?

Muted, yes, with three caveats.

- **Chrome** always allows muted autoplay. Autoplay *with sound* needs the user to have interacted with your site, a high Media Engagement Index (desktop), or an installed site ([Chrome's autoplay policy](https://developer.chrome.com/blog/autoplay)).
- **Safari on iOS** allows autoplay without a tap when the video is muted or has no audio track. It pauses the video if it scrolls out of view, and pauses it if it gains sound without a user gesture ([WebKit](https://webkit.org/blog/6784/)).
- **iOS Low Power Mode** can block autoplay altogether. Developers have reported it on the [WebKit bug tracker](https://bugs.webkit.org/show_bug.cgi?id=216887), with no official statement on whether it's intended. This is the main reason the poster image matters: some visitors will only ever see it.

**Strip the audio track** (`-an` in ffmpeg). A hero video has no use for sound, and a silent track is still an audio track to the browser.

## MP4, WebM or GIF? What we measured

We exported one 7.5-second loop from Lumora at 960x540 and encoded it five ways with ffmpeg. Same source, same length:

| Format | Settings | File size |
|---|---|---|
| WebM (VP9) | 30 fps, CRF 40 | 279 KB |
| MP4 (H.264) | 30 fps, CRF 28, faststart | 354 KB |
| GIF | 15 fps, 960 px wide, optimised palette | 15.8 MB |
| GIF | 12 fps, 640 px wide, optimised palette | 7.1 MB |
| Poster (JPEG) | first frame, 960x540 | 17 KB |

Even the smaller, choppier GIF was about 25 times the size of the full-quality WebM. That matches web.dev's own example, where a [3.7 MB GIF became a 551 KB MP4 and a 341 KB WebM](https://web.dev/articles/replace-gifs-with-videos). GIFs have their place (a GitHub README, an email), but not on a page you control.

The ffmpeg commands we used:

```bash
# MP4 (H.264), no audio, index moved to the front so playback starts early
ffmpeg -i hero-master.mp4 -an -c:v libx264 -crf 28 -preset slow \
  -pix_fmt yuv420p -movflags +faststart hero.mp4

# WebM (VP9), no audio
ffmpeg -i hero-master.mp4 -an -c:v libvpx-vp9 -crf 40 -b:v 0 hero.webm

# Poster from the first frame
ffmpeg -i hero-master.mp4 -frames:v 1 -q:v 5 hero-poster.jpg
```

`-movflags +faststart` matters for MP4: it runs a second pass that moves the index (the "moov atom") to the beginning of the file, as the [ffmpeg docs](https://ffmpeg.org/ffmpeg-formats.html) describe, so the browser can start playing before the whole file has downloaded. It's off by default.

## How big should the hero video file be?

There's no official limit, but the video competes with everything else on your page. Our rules of thumb:

- **Resolution:** match the size it's shown at. A hero that's 960 px wide on most screens doesn't need a 1920x1080 file; 1280x720 covers most layouts, and 1920x1080 is the ceiling.
- **Frame rate:** 30 fps. 60 fps doubles the frames for little visible gain in a UI demo.
- **Size:** aim for under 1 MB for a 10 to 15 second loop. Our 7.5-second test landed at 354 KB (MP4) and 279 KB (WebM) at 960x540.
- **Length:** 10 to 20 seconds. Longer loops cost bytes nobody watches. See [how long a demo video should be](/blog/how-long-should-a-demo-video-be) for other channels.

## The poster frame and Core Web Vitals

For a `<video>`, Largest Contentful Paint uses ["the poster image load time or first frame presentation time … whichever is earlier"](https://web.dev/articles/lcp). If the hero video is the biggest thing above the fold, it is probably your LCP element, and a good LCP is 2.5 seconds or less.

So the poster is a performance asset, not decoration:

- Make it the **exact first frame** of the video, so the swap is invisible.
- Keep it small: our 960x540 JPEG poster was 17 KB.
- Give it the same dimensions as the video, so nothing shifts.

## Mobile: a vertical source and less data

On a phone, a 16:9 video in a narrow column is tiny. You can serve a different file by screen size: the `media` attribute on `<source>` inside `<video>` works again in Firefox 120, Chrome 120 and Safari, which never dropped it, as [Scott Jehl documented](https://scottjehl.com/posts/using-responsive-video/) when support returned in late 2023.

```html
<video autoplay muted loop playsinline poster="/hero-poster.jpg">
  <source media="(max-width: 640px)" src="/hero-vertical.mp4" type="video/mp4">
  <source src="/hero.webm" type="video/webm">
  <source src="/hero.mp4" type="video/mp4">
</video>
```

A 9:16 or 1:1 cut of the same edit usually reads better on a phone than a scaled-down landscape one. Our [demo video sizes guide](/blog/demo-video-sizes-specs) has the dimensions.

## Accessibility: a pause button and reduced motion

Two things are easy to forget and cheap to add.

**A pause button.** WCAG 2.2 success criterion 2.2.2 (Level A) says that for moving content that starts automatically, lasts more than five seconds and appears alongside other content, ["there is a mechanism for the user to pause, stop, or hide it"](https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide). A looping hero video meets all three conditions.

```js
const video = document.querySelector('.hero-video');
const button = document.querySelector('.hero-pause');
button.addEventListener('click', () => {
  if (video.paused) { video.play(); button.textContent = 'Pause'; button.setAttribute('aria-pressed', 'false'); }
  else { video.pause(); button.textContent = 'Play'; button.setAttribute('aria-pressed', 'true'); }
});
```

**Reduced motion.** People who turn on "reduce motion" in their operating system get `prefers-reduced-motion: reduce`, and web.dev lists [refraining from autoplaying videos](https://web.dev/articles/prefers-reduced-motion) as one way to respect it. Show the poster and let them press play:

```js
if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
  video.removeAttribute('autoplay');
  video.pause();
  button.textContent = 'Play';
}
```

Add an `aria-label` that says what the video shows, and keep the headline and call to action in real HTML text, not burned into the video.

## What should the hero video show?

The technical setup is the easy part. What works in the frame:

- **The product, not a montage.** One action and its result: an invoice sent and paid, a dashboard filling in, a design exported.
- **Readable at a glance.** Visitors read your headline while the video plays, so big UI, little text, and one callout at most.
- **A seamless loop.** End where you started: same camera position, same screen. A visible jump every 12 seconds looks like a glitch.
- **Calm motion.** A slow push in or a gentle float works under a headline; whip pans and spins compete with it. See [camera moves for UI videos](/blog/camera-moves-for-ui-videos).

If you don't have footage, you don't need a screen recording: you can [make the hero loop from screenshots](/blog/demo-video-from-screenshots).

## Making the hero video in Lumora

[Lumora](/) (our tool) exports MP4, WebM and GIF from the same edit, in 16:9, 9:16 and 1:1, so the desktop and phone sources come from one project. For a hero, pick a calm look (Keynote or Studio), set the length to 10 to 15 seconds, export WebM and MP4 without music, and take the first frame as the poster. You can also embed a Lumora video with one line of code, but for a hero section, self-hosting the files with the tag above gives you the most control over loading.
