HTML Video Element Table
| Attribute | What it does | Field note |
|---|---|---|
controls | Native playback UI | The accessible player for free - build custom only with real reasons |
autoplay + muted + playsinline | The silent-autoplay trio | ALL THREE together = autoplay allowed - unmuted never autoplays |
poster | Pre-play frame | Saves the first-frame download as your LCP image |
preload="metadata" | Fetch header info only | none = nothing; auto = maybe everything - metadata is the polite default |
<source> elements | Format fallbacks | Browser takes the FIRST it can play - list AV1, then WebM, then MP4 |
width/height | Ratio reservation | Same CLS math as img - reserve before the bytes arrive |
muted / volume | Audio control | muted reflects the attribute; volume is JS-only (0-1 property) |
loop / playbackRate | Loop and speed | playbackRate via JS - the player feature set is scriptable |
The video element's central law is the AUTOPLAY POLICY: browsers allow autoplay only for SILENT video, which is why the working incantation is the trio autoplay + muted + playsinline - drop muted and the browser blocks it, drop playsinline and iOS hijacks the screen with a fullscreen takeover.
Bottom line: source elements give format fallbacks without JavaScript - the browser takes the FIRST it can play, so list modern codecs (AV1, WebM) before the universal MP4. poster supplies the pre-play frame (saving your hero from a first-frame download), and preload=metadata is the polite default between 'nothing' and 'maybe everything'.
The honest part: the native controls are the accessible player for free - keyboard operable, screen-reader labeled, captions-wired. Build a custom player only with real design reasons, and then YOU own the keyboard map, the focus states and the caption toggle that controls shipped yesterday.
How to use
- Ship the autoplay hero: <video autoplay muted playsinline loop> with a poster - background video that starts everywhere, no sound by contract.
- List sources old-new: <source src=a.av1 type=video/av1><source src=a.webm type=video/webm><source src=a.mp4 type=video/mp4> - first playable wins, no JavaScript.
- Reserve the box: width and height attributes on video, same as img - the aspect-ratio box kills layout shift before the metadata arrives.
Frequently asked questions
Why does autoplay work on my machine but not for users?
The autoplay policy evaluates per context, not per attribute. Muted videos autoplay essentially everywhere; UNMUTED autoplay requires engagement signals (the user has played media on your site before, or has site-specific settings) - so your dev profile, full of history, plays and their fresh browser does not. iOS adds the playsinline requirement: without it, autoplaying video takes over the fullscreen video controller. The robust pattern never relies on unmuted autoplay: start muted with a visible unmute button, or gate playback behind a play click. detect autoplay (video.play() promise rejection) if the behavior matters.
What does preload actually control, and which value should I ship?
A hint with three levels: none downloads nothing until play (minimal cost, slow start), metadata fetches duration and dimensions only (enables ratio reservation and player UI), auto lets the browser download as it pleases (fast start, potentially heavy). The default is browser-defined 'auto-ish', which is why background pages with many videos can silently download megabytes. Ship metadata for player-attached videos (the polite default that still knows the dimensions), none for below-the-fold or click-to-play, and reserve space with dimensions regardless - the hint can be ignored by the browser, the attributes cannot.
How do source fallbacks actually get chosen?
Top-down first-match. The browser evaluates source elements in order and takes the FIRST whose type attribute it can decode - type is what lets it decide WITHOUT downloading (if you omit type, it must fetch and sniff, wasting requests). The ordering strategy is therefore newest-and-best first: AV1 or WebM (smaller at equal quality) before the universal MP4/H.264 fallback that every device including old ones decodes. The video element itself is the fallback of last resort - its inner content renders in engines that do not know video at all, which is where a download link or poster message lives.
When is a video element the wrong tool versus CSS animation or GIF?
For short decorative loops, a muted autoplaying video is actually the RIGHT tool - it is smaller than GIF (real codecs beat GIF's 256-color palette by an order of magnitude) and smoother. Video becomes wrong when: the visual is simple enough for CSS (a gradient pulse costs zero bytes and never buffers), when audio must sync precisely (Web Audio's domain), or when the 'video' is interactive (that is canvas or a real player). The GIF comparison deserves emphasis: converting a GIF to a muted looped MP4 or WebM typically cuts size 90%+ - the animated GIF survives on the web only as a format for chat apps, not for pages.