Chunked Video Playback with HTML5 Media Source Extensions
Learn how Media Source Extensions let you stream video in small chunks, improving load times and enabling adaptive bitrate in modern browsers.
08 Jun 2026, 18:53 UTC

Problem: Full‑file video download stalls playback
When a browser receives a single video file, it must download the entire file before it can start playing. On slow or variable networks this creates long buffering periods and a poor user experience.
Thesis: Media Source Extensions enable chunked, adaptive streaming
The HTML5 Media Source Extensions (MSE) API lets JavaScript append small, sequential buffers to a MediaSource object. This lets the app fetch and play video segments on demand, supporting adaptive bitrate switching and progressive loading.
How MSE works
First, the browser checks if the chosen MIME type (for example video/webm) is supported via MediaSource.isTypeSupported(). If supported, a MediaSource instance is created and attached to a video element. Then a SourceBuffer is created for the MIME type, and each video segment—typically a few seconds of encoded data—is fetched as an ArrayBuffer and appended with SourceBuffer.appendBuffer(). The browser stitches the segments together, handles key‑frame alignment, and updates the playback head.
Worked example
// 1. Verify MSE support for the chosen MIME type
if (!MediaSource.isTypeSupported('video/webm')) {
console.error('MSE not supported for video/webm');
return;
}
// 2. Create MediaSource and attach to video
const mediaSource = new MediaSource();
document.querySelector('video').src = URL.createObjectURL(mediaSource);
// 3. Add a SourceBuffer for webm
const sourceBuffer = mediaSource.addSourceBuffer('video/webm');
// 4. Fetch and append segments (replace placeholders)
async function loadSegments(manifestUrl) {
const manifestResponse = await fetch(manifestUrl);
const manifest = await manifestResponse.json(); // assume HLS or DASH format
for (const seg of manifest.segments) {
const segResponse = await fetch(seg.url); // placeholder for segment URL
const segData = await segResponse.arrayBuffer();
sourceBuffer.appendBuffer(segData);
// optionally wait for sourcebufferupdate event to ensure proper timing
}
}
loadSegments('[MANIFEST_URL]').catch(console.error);
Replace [MANIFEST_URL] with the URL of a test HLS or DASH manifest. The code uses fetch to retrieve each segment as an ArrayBuffer and appends it to the SourceBuffer. No special permissions are required beyond normal network access.
Trade‑offs and limitations
MSE works only in modern browsers—Chrome, Edge, Safari ≥ 14, and Firefox ≥ 44. Older browsers fall back to native loading, which re‑introduces the full‑file download problem.
Managing the SourceBuffer lifecycle can be tricky; adding or removing buffers, handling error events, and ensuring key‑frame continuity require careful coding to avoid stalls.
Large segment sizes increase memory pressure and may trigger garbage‑collection spikes. Keeping segment duration between 2 and 10 seconds is recommended for smooth playback.
Actionable closing
To verify that MSE is working, open the console and run MediaSource.isTypeSupported('video/webm'). Load a small test manifest and watch the Network tab—you should see multiple small requests for segment files rather than a single large download. If playback starts after a few seconds without a full file fetch, the chunked approach is successful. For browsers lacking MSE support, provide a fallback <video> element that loads the full file or use a server‑side transcoding service.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.