Why HLS.js?
Only Safari natively supports HLS. Chrome, Firefox, and Edge cannot play M3U8 URLs directly. HLS.js uses the Media Source Extensions (MSE) API to enable HLS playback in these browsers, covering over 95% of users.
Quick Start: 10 Lines of Code
<video id="video" controls width="640"></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const video = document.getElementById('video');
const src = 'https://example.com/video/index.m3u8';
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = src; // Safari native
}
</script>
Hls.isSupported() detects MSE support. Safari takes the else branch and plays natively — no need to load HLS.js at all, saving bandwidth.Fixing CORS Issues
CORS is the most common blocker in HLS.js development. The M3U8 file and TS segments must be served with the appropriate headers:
# Nginx config
location ~* \.(m3u8|ts)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
add_header Cache-Control "no-cache"; # For M3U8; TS can be cached longer
}
Error Handling and Recovery
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
hls.startLoad(); // retry
break;
case Hls.ErrorTypes.MEDIA_ERROR:
hls.recoverMediaError(); // attempt recovery
break;
default:
hls.destroy();
break;
}
}
});
ABR Configuration
const hls = new Hls({
startLevel: -1, // Auto-select initial quality
maxMaxBufferLength: 60, // Max buffer (s); use 30 for live
enableWorker: true, // Move demuxing to Web Worker
abrBandWidthUpFactor: 0.7, // Conservative upswitch threshold
liveSyncDurationCount: 3, // Live sync target (segments)
});
Manual Quality Switching
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
data.levels.forEach((level, i) => {
console.log(`Level ${i}: ${level.height}p, ${(level.bitrate/1000).toFixed(0)} kbps`);
});
});
hls.currentLevel = 1; // Lock to level 1
hls.currentLevel = -1; // Back to auto
For a working reference implementation, see the StreamFlow online player, which is built on HLS.js.
- Use a CDN-hosted HLS.js (e.g., jsDelivr) rather than self-hosting — users may already have it cached from other sites.
enableWorker: truesignificantly improves smoothness by moving TS demuxing off the main thread. Always enable it in production.- For live streams, keep
maxMaxBufferLengthunder 30 seconds to prevent latency buildup. - If multiple players coexist on a page, call
hls.stopLoad()when a player is off-screen to save bandwidth. - Enable debug logging during development:
new Hls({ debug: true })— the console will show detailed ABR switching and segment download events.