Overview
Browser video ranges from a single element with a source attribute to a full adaptive streaming stack, and choosing the wrong end of that range costs you either playback reliability or a great deal of unnecessary complexity. This guide covers when progressive download is genuinely sufficient, what adaptive streaming actually buys, why the same file plays in one browser and not another, and the small details (faststart, codec strings, CORS) that account for most playback bugs.
What You Need
- Media encoded in a browser-compatible codec and container
- A server or CDN that supports range requests
- Correct MIME types configured, which is a common oversight
- Test devices beyond your development machine, particularly mobile
- For adaptive streaming: a packager and a player library
- Somewhere to check playback behaviour on a constrained connection
Steps
Start with a plain video element and progressive download
For short clips, single-quality content, and anything where audience bandwidth is not the constraint, a video element pointing at an MP4 is sufficient, requires no library, and is keyboard accessible by default. Adaptive streaming is a solution to a problem you may not have, and adopting it unnecessarily adds a packaging step and a player dependency.
Put the index at the front of your MP4
MP4 files store an index that a player needs before playback can begin, and by default it is written at the end. Without moving it to the front, commonly called faststart, the browser must download the entire file before it can start. This single flag accounts for an enormous share of "the video takes forever to start" reports.
Encode for compatibility before efficiency
H.264 video with AAC audio in an MP4 container remains the broadest-support combination by a wide margin. More efficient codecs compress substantially better and have uneven support and uneven hardware decoding, which affects battery and performance on mobile. Offer efficient formats as an addition, not a replacement.
Adopt adaptive streaming when bandwidth varies meaningfully
HLS and DASH split media into segments at several quality levels so the player can switch as conditions change. This is worth the complexity for long-form content, for mobile audiences on variable connections, and for live. It requires packaging your media into segments and a manifest, plus a player library on most browsers.
Get the codec string right in your manifests and source elements
Browsers decide whether they can play something from a codec string that specifies profile and level, not just the container. An imprecise or wrong string causes a browser to reject media it could actually play, or to attempt media it cannot. This is a frequent and frustrating source of device-specific failures.
Check the boring infrastructure before debugging the player
Range requests must be supported for seeking to work. MIME types must be correct or browsers refuse the file. CORS headers must permit the request when media is served from another origin. A large share of playback bugs are one of these three rather than anything to do with the media itself.
Pro Tips
- Always enable faststart on MP4s intended for the web. It is one flag and it fixes the most-reported symptom.
- Test on a real phone on cellular, not on desktop with a throttled connection. They fail differently.
- Serve an H.264/AAC MP4 rendition alongside anything more efficient, as the compatibility fallback.
- Check range request support early, without it, seeking silently does not work.
- The native video element is accessible by default. Any custom player has to earn that back.
Knowledge Base
What You'll Learn
Playback problems cluster into a few causes. Below: what adaptive streaming actually does, and the container-versus-codec distinction behind most compatibility complaints.
What Adaptive Streaming Actually Buys
Adaptive bitrate streaming is frequently adopted because it sounds professional rather than because the problem it solves is present.
The mechanism: your media is encoded at several quality levels and split into short segments, with a manifest describing what is available. The player measures throughput and buffer state and chooses which quality level to request for each successive segment, switching mid-playback as conditions change.
The benefit is that a viewer on a poor connection gets continuous playback at reduced quality rather than a stalling high-quality stream, and a viewer on a good connection gets full quality without you having to guess. It also makes live streaming feasible over ordinary HTTP infrastructure.
The cost is a packaging step producing multiple renditions and a manifest, storage for all those renditions, and a player library on browsers without native support. For a two-minute clip watched on office wifi, none of that pays for itself. For a forty-minute video watched largely on phones, it clearly does.
Container Versus Codec, and Why the Same File Fails Elsewhere
The single most common playback confusion is treating the file extension as describing what a browser can play.
A container: MP4, WebM, MOV, describes how streams and metadata are organised. A codec. H.264, HEVC, AV1, AAC, Opus, is the compression used for the stream inside it. The extension names the container and says almost nothing about playability.
This produces the classic report: the same MP4 plays on one machine and not another. The container is fine everywhere. The codec inside is not supported on the failing device, frequently for licensing rather than technical reasons.
It also explains why the codec string matters. A browser asked whether it can play a type answers based on the codec, profile, and level specified: so an incomplete string leads it to guess, and it guesses conservatively.
The practical defence is to serve an H.264/AAC MP4 rendition as a universally-supported fallback, declare codec strings precisely, and test on real devices rather than assuming the desktop result generalises.
Where This Fits
This guide covers one specific part of building media applications. The wider picture, why media workloads break ordinary web architecture, and the upload, job, and toolchain patterns that handle them, is in Building Media Applications: A Developer Primer, which frames the discipline as a whole and links out to the detailed guides underneath it, including this one. If you are starting from scratch rather than solving a specific problem, read that first and come back here.
FAQ
Q: Do I need HLS or DASH for my site?
A: Only if bandwidth varies meaningfully across your audience or your content is long-form or live. For short clips at a single quality, a plain video element with a faststart MP4 is simpler, has no dependencies, and is accessible by default. Adaptive streaming solves a real problem you may not have.
Q: Why does my video take a long time to start playing?
A: Most often because the MP4 index is at the end of the file, so the browser must download all of it before playback can begin. Re-encode or remux with faststart so the index is at the front. The other common cause is the server not supporting range requests.
Q: Why does the same file play on desktop but not on a phone?
A: Almost always codec support rather than the container. The extension names the container while the codec inside determines playability, and support varies by browser and platform partly for licensing reasons. Provide an H.264/AAC MP4 rendition as a compatibility fallback.
Q: Should I build a custom player?
A: Only with a concrete reason such as branding or analytics, and knowing you are taking on accessibility work the native element provides free: keyboard operation, labelled controls, and caption support. Adopting a reputable accessible player library is usually cheaper than making a custom one compliant.
Translate this page
- Español
- 简体中文
- हिन्दी
- العربية
- Português
- Français
- Deutsch
- 日本語
- Русский
- Bahasa Indonesia
- 한국어
- Italiano
- Türkçe
- Tiếng Việt
- Polski
- Nederlands
Machine translation provided by Google Translate, on Google’s servers. We do not check these translations and they will get technical terms wrong. The English page is the authoritative one. Following a link sends this page’s address to Google. Your browser may also offer to translate this page itself, which keeps the request on your device.