Video and Media
Date: 2026-08-17
Video is the heaviest thing most retail sites ship, and the default
<video>attributes are wrong for nearly every use of it. The two decisions that matter are what downloads before anyone presses play, and whether the thing is a video at all.
Video delivery is how video reaches the page — the <video> element’s loading attributes, the file’s encoding, and whether it streams adaptively — which decides what downloads, when, and how much.
The attributes, and what the defaults cost
<!-- an autoplaying background or product loop -->
<video
poster="/product-loop.avif" <!-- shown before playback; also the LCP
candidate, so optimise it as an image -->
preload="none" <!-- ← the important one. see below -->
autoplay muted playsinline loop
width="1280" height="720"> <!-- reserves space, prevents layout shift -->
<source src="/loop.webm" type="video/webm">
<source src="/loop.mp4" type="video/mp4">
</video>preload is where the weight goes, and the default is the problem:
preload="none" fetches nothing until play. metadata unknown.
← the right default for anything below the fold
or click-to-play
preload="metadata" fetches duration and dimensions only, a few KB
← sensible when you need the duration displayed
preload="auto" the browser MAY fetch the whole file
← on a 24 MB product video, before anyone
pressed play. this is the accident
autoplay requires muted — browsers block autoplay with sound, and a video that silently fails to play is a common bug. playsinline prevents iOS taking over the screen, which is essential for a background loop.
The one that isn’t really a video
Most “videos” on retail sites are silent, short, looping and decorative — a fabric close-up, a garment moving, a product rotating. For these, the format question is upstream of everything else:
animated GIF ✗ enormous. a 3-second loop is often 4–8 MB.
never correct in 2026
animated WebP/AVIF better, still image-codec compression
MUTED LOOPING VIDEO ✓ 10–20× smaller than the equivalent GIF
(WebM/MP4) video codecs exist for exactly this
CSS/SVG animation ✓ best of all, where the motion is simple
Replacing an animated GIF with a muted MP4 loop is one of the largest single-file wins available on sites that still have them, and it’s a mechanical change — Modern Image Formats.
Streaming, and when it’s worth it
PROGRESSIVE (a plain .mp4) ADAPTIVE
HLS — HTTP live streaming
DASH — dynamic adaptive streaming over HTTP
one file, one bitrate many renditions, segmented
served from any static host needs a packager and a manifest
simple, cacheable player library required (~50–100 KB JS)
user on a poor connection buffers player switches down automatically
user on fibre gets the same file fast connections get a better rendition
fine for: short loops, <30s worth it for: long-form, >2 min,
clips, anything under ~10 MB or a video that's the point of
the page
Adaptive streaming has a real fixed cost — the player JavaScript, the packaging pipeline, the manifest. For a 6-second product loop it’s absurd; for a 4-minute brand film it’s the difference between watchable and abandoned.
Prefer a hosted video service for anything long-form. Encoding ladders, adaptive delivery, DRM and global distribution are not worth building — and this is a rare case where the vendor’s version is straightforwardly better than what you’d assemble.
What it does to the metrics
- The poster frame is often the LCP element. It’s an image, so all of Image Optimisation applies — right dimensions, modern format, not lazy-loaded
- Video itself is usually not the LCP element, so a heavy video hurts by consuming bandwidth that the LCP resource needed, rather than by being slow itself
- Always set
widthandheight, or the player collapses to zero height and expands on load — Cumulative Layout Shift - Autoplaying video competes for the main thread on decode, which affects interaction responsiveness on lower-end devices — Interaction to Next Paint
- Lazy-load below-fold video —
preload="none"plus loading the player only when it scrolls near the viewport — Lazy Loading
Embedded third-party players
A YouTube or Vimeo embed is a third-party iframe that loads a substantial amount of JavaScript, sets cookies and runs before anyone has expressed interest in watching.
The facade pattern is the fix: render the poster image and a play button as plain HTML, and only inject the real embed on click.
default embed ~500 KB–1 MB, third-party cookies set on load
facade ~30 KB (one image), embed loads on click only
Consent applies here too — an embed that sets tracking cookies before consent is a compliance problem as well as a performance one — Consent Management, Third-Party Scripts.
Accessibility, which isn’t optional
- Captions for anything with speech — a
<track kind="captions">element, not burnt-in text - A transcript for long-form, which also serves SEO
- Never autoplay with sound, and provide a pause control for anything looping
- Respect
prefers-reduced-motion— an autoplaying background loop is exactly what that setting exists for, and honouring it means not playing rather than playing slower — Reduced Motion, WCAG
Where it interacts
- Image Optimisation — the poster frame is an image and usually the LCP element
- Lazy Loading — the main mitigation for below-fold media weight
- Third-Party Scripts — embedded players are among the heaviest third parties on a typical page
- Reduced Motion — the accessibility constraint on anything that moves without being asked to