A hero video that does not destroy your LCP
A video hero is not slow because it is a video. It is slow because of four specific decisions, each of which has a fix that costs nothing.
Every few weeks someone sends us a beautiful site with a Lighthouse score in the thirties and asks whether cinematic web is simply incompatible with performance. It is not. It is incompatible with four specific habits, and all four are cheap to fix.
The thing almost everyone gets wrong first
Chrome does not consider video frames as candidates for Largest Contentful Paint. It does consider the video's poster image.
This single fact reorders the whole problem. Your LCP is not the video. Your LCP is a still image, and still images are a solved problem. Which means the sequence is:
- Make the poster small and beautiful.
- Get the poster to the browser as fast as possible.
- Let the video arrive whenever it arrives.
Sites fail this by having no poster at all, or by having a poster that is a 900KB PNG export from the edit.
Poster strategy
The poster is a design decision, not a fallback. It is the first frame anyone sees on a slow connection, and on a genuinely bad connection it may be the only frame.
- Export it from the actual first frame of the loop so there is no visible pop when playback starts.
- Serve AVIF first, WebP second, JPEG last, via
<picture>if you need the fallback chain, or via a CDN that negotiates byAcceptheader. - Budget 40 to 120KB. A well-tuned AVIF of a dark cinematic frame often lands under 60KB because dark, low-detail imagery compresses beautifully.
- Add
fetchpriority="high"to the poster if you serve it as a separate<img>, and make sure nothing else on the page is also claiming high priority.
The encode ladder
Ship more than one file and let the browser choose.
<video
poster="/media/hero-poster.avif"
preload="none"
muted
playsinline
loop
disablepictureinpicture
aria-hidden="true">
<source src="/media/hero.av1.webm" type="video/webm; codecs=av01.0.05M.08">
<source src="/media/hero.vp9.webm" type="video/webm; codecs=vp9">
<source src="/media/hero.h264.mp4" type="video/mp4">
</video>
Order matters: the browser takes the first source it can decode, so the most efficient codec goes first and the universal fallback goes last.
A working encode set, as a starting point rather than gospel:
# H.264 fallback. faststart is not optional
ffmpeg -i master.mov -vf "scale=1920:-2" -c:v libx264 -crf 24 -preset slow \
-pix_fmt yuv420p -an -movflags +faststart hero.h264.mp4
# VP9
ffmpeg -i master.mov -vf "scale=1920:-2" -c:v libvpx-vp9 -crf 34 -b:v 0 \
-row-mt 1 -an hero.vp9.webm
# AV1: slower to encode, meaningfully smaller
ffmpeg -i master.mov -vf "scale=1920:-2" -c:v libsvtav1 -crf 34 -preset 6 \
-an hero.av1.webm
Two details in there earn their place:
-movflags +faststart. This moves the moov atom (the index) from the end of the MP4 to the front. Without it a browser has to download the entire file before it can display a single frame. It is the single most common cause of "the video takes forever then appears all at once."
-an. Strip the audio. A muted background video does not need an audio track, and the track costs bytes and a decoder.
Preload order
The instinct is preload="auto" because you want the video ready. In practice that makes the video compete with the poster, the stylesheet and the font for the browser's early connections, and delays exactly the paint you are being graded on.
Better: preload="none", then start fetching after the page has painted.
<video id="hero" poster="/media/hero-poster.avif" preload="none" muted playsinline loop></video>
<script>
const v = document.getElementById('hero');
const start = () => {
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
if (navigator.connection?.saveData) return;
if (/2g/.test(navigator.connection?.effectiveType || '')) return;
v.preload = 'auto';
v.load();
v.play().catch(() => {}); // autoplay can still be refused; the poster stays
};
if (document.readyState === 'complete') start();
else addEventListener('load', start, { once: true });
</script>
Three guards in nine lines: reduced motion, data saver, and a slow connection. Each one turns a site that punishes a user into one that degrades to a still image, which was always going to be beautiful anyway.
The attributes that are load-bearing
muted and playsinline together are what allow autoplay at all on iOS. Miss either one and Safari refuses, silently, and you ship a black box to every iPhone.
loop on a seamless cut. disablepictureinpicture to stop the browser offering a PiP control over your art direction. aria-hidden="true" on a purely decorative hero, so a screen reader does not announce an empty media element.
Length is a budget decision
A ten-second seamless loop at high quality reads as more expensive than a forty-second sequence at low quality. It is also four times smaller.
If the brief genuinely needs a longer sequence, put it below the fold or behind an interaction, where it is not competing with first paint. The hero has one job, which is to be immediately, obviously good on the first screen.
Mobile is a different film
Below 768px, be ruthless:
- Serve a separate, shorter, narrower encode, not the desktop file scaled down in CSS. The bytes are what matter, not the display size.
- Consider serving only the poster on mobile. A still frame with a slow parallax often reads better on a phone than a video that stutters.
- Watch the URL bar.
100vhon mobile Safari is not the visible height. Use100dvhwith a100vhfallback, or you will ship a hero with a mystery gap.
The launch checklist
- Poster exists, is AVIF or WebP, and is under 120KB.
- Poster is the actual first frame of the loop.
+faststartapplied to every MP4.- Audio track stripped.
- AV1 or VP9 offered before H.264.
preload="none"with a post-load start.muted,playsinline,loopall present.prefers-reduced-motionrespected.- Data saver and slow connections fall back to the poster.
- A separate, smaller mobile encode.
- Hero file under 2MB, ideally under 1.2MB.
- Measured on a real mid-range Android on a throttled connection, not on your laptop.
Done properly, a full-bleed cinematic hero and a green Core Web Vitals report are not in tension. The sites that fail are not failing because they chose motion. They are failing because they shipped the edit master.
Questions we get asked
Does a background video count as the Largest Contentful Paint element?
No. Chrome does not treat video frames as LCP candidates, but it does treat the video's poster image as one. That means the fastest route to a good LCP on a video hero is to serve a small, well-compressed poster and let the video arrive afterwards.
How large should a website hero video be?
Under 2MB for anything above the fold, and ideally closer to 1MB. A ten-second seamless loop at 1920x1080, encoded in AV1 or well-tuned H.264 with a moderate CRF, comfortably fits that budget. Longer films belong below the fold or behind a click.
Should I use preload auto on a hero video?
Usually not. preload="auto" makes the video compete with the poster image and the stylesheet for the browser's limited early connections, which delays the very paint you are being measured on. preload="none" with a good poster, then loading the video after the load event, measures better on nearly every real site.
MP4 or WebM for a hero video?
Ship both, MP4 (H.264) last as the universal fallback and AV1 or VP9 in WebM first for the browsers that support them. The browser picks the first source it can decode, so ordering matters. AV1 typically saves 30 to 50 percent of the bytes at equal perceived quality, at the cost of much longer encode time.
Last verified 4 September 2026 by MOTIONwebsites Editorial. Figures and model capabilities in this field change quickly; we re-check every article on this site on a rolling schedule and update the date above when we do.
Cinema, minus the studio.
MOTIONwebsites builds cinematic websites with proprietary AI. No crew, no location, no six-figure production budget, and it ships in days instead of months.