If you have ever tried to build a video player that works across every browser, handles both DASH and HLS, supports DRM, and lets users download content for offline viewing, you know the pain. You end up stitching together multiple libraries, writing platform-specific code, and losing sleep over edge cases. Shaka Player is Google's answer to that mess. It is an open-source JavaScript library that plays adaptive media formats directly in the browser using standard web APIs — no Flash, no plugins, no tears. Whether you are building an OTT platform, an educational site with offline downloads, or a live sports streaming app, shaka-player has you covered.
What Makes It Tick
Shaka Player packs a serious feature list into a single, dependency-free package:
- Unified format support — DASH and HLS playback from one library, including low-latency variants (LL-DASH and LL-HLS)
- Multi-DRM — Widevine, PlayReady, FairPlay, and ClearKey all work out of the box through the Encrypted Media Extensions API
- Offline storage — Download content to IndexedDB for playback without a network connection
- Ad insertion — Built-in support for IMA SDK, IMA DAI, AWS MediaTailor, VAST, and VMAP
- VR playback — Equirectangular and cubemap projections for 360-degree video
- Thumbnails — DASH-IF image adaptation sets, HLS image playlists, and external WebVTT sprites
- Broad device support — Desktop browsers, mobile, Chromecast, Tizen, WebOS, Xbox, and more
- Optional UI — A built-in controls layer you can use or ignore entirely
The library is backed by Google, Ateme, and Paramount, which means it is not going anywhere anytime soon. Version 5.0.0 dropped in February 2026 with a clean API surface and the removal of all deprecated v4 features, while v4.16.x continues as an LTS branch.
Getting It On Your Machine
Install via npm:
npm install shaka-player
Or with yarn:
yarn add shaka-player
Shaka ships several build variants. The full UI build is shaka-player/dist/shaka-player.ui.js, the compiled player without UI is shaka-player/dist/shaka-player.compiled.js, and there are DASH-only and HLS-only builds if you want to trim the bundle.
Your First Stream in 30 Seconds
Spinning Up the Player
The basic setup is refreshingly straightforward. Create a video element, instantiate the player, and load a manifest URL.
import shaka from "shaka-player";
shaka.polyfill.installAll();
const video = document.getElementById("video") as HTMLVideoElement;
const player = new shaka.Player(video);
player.addEventListener("error", (event: shaka.PlayerEvents.ErrorEvent) => {
console.error("Shaka error:", event.detail);
});
await player.load("https://example.com/stream/manifest.mpd");
console.log("Stream is playing!");
The polyfill.installAll() call patches browser inconsistencies so you do not have to think about them. After that, load() handles manifest parsing, codec negotiation, and adaptive bitrate selection automatically.
Checking Browser Support
Before creating the player, it is good practice to verify the browser can handle it:
import shaka from "shaka-player";
shaka.polyfill.installAll();
if (shaka.Player.isBrowserSupported()) {
const video = document.getElementById("video") as HTMLVideoElement;
const player = new shaka.Player(video);
await player.load("https://example.com/live/stream.m3u8");
} else {
console.warn("Browser not supported for adaptive streaming");
}
Notice that the same player handles both .mpd (DASH) and .m3u8 (HLS) manifests without any configuration changes.
Tuning the Dials
Shaka has a deep configuration system. You can tweak buffering behavior, ABR strategy, and streaming preferences:
player.configure({
streaming: {
bufferingGoal: 60,
rebufferingGoal: 2,
bufferBehind: 30,
},
abr: {
enabled: true,
defaultBandwidthEstimate: 1_000_000,
},
});
The bufferingGoal sets how many seconds of content the player tries to keep buffered ahead, while rebufferingGoal controls how much buffer is needed before playback resumes after a stall. These defaults work for most cases, but live streams and mobile connections often benefit from tuning.
Unlocking the Vault
DRM Without the Drama
Setting up DRM is where many players become painful. Shaka keeps it clean by being key-system-agnostic — you just tell it where your license servers are:
player.configure({
drm: {
servers: {
"com.widevine.alpha": "https://license.example.com/widevine",
"com.microsoft.playready": "https://license.example.com/playready",
"com.apple.fps": "https://license.example.com/fairplay",
},
advanced: {
"com.apple.fps": {
serverCertificateUri: "https://license.example.com/fairplay-cert",
},
},
},
});
await player.load("https://example.com/protected/manifest.mpd");
Shaka auto-detects which key system the browser supports and picks the right one. On Chrome it will use Widevine, on Safari it will use FairPlay, and on Edge it can use PlayReady. You configure all three and let the library sort it out.
Taking Video Offline
The offline storage API lets users download content for later playback, which is essential for mobile apps and PWAs:
import shaka from "shaka-player";
const storage = new shaka.offline.Storage();
storage.configure({
offline: {
progressCallback: (content, progress) => {
console.log(`Download: ${(progress * 100).toFixed(1)}%`);
},
},
});
const stored = await storage.store("https://example.com/movie/manifest.mpd");
console.log("Stored content:", stored.offlineUri);
const player = new shaka.Player(
document.getElementById("video") as HTMLVideoElement
);
await player.load(stored.offlineUri!);
The stored content lives in IndexedDB and persists across sessions. You can list all stored content with storage.list() and remove it with storage.remove(). The progress callback gives you everything you need to build a download manager UI.
Going Live with Low Latency
For live sports or real-time events, low-latency streaming keeps your viewers close to the live edge:
player.configure({
streaming: {
lowLatencyMode: true,
inaccurateManifestTolerance: 0,
rebufferingGoal: 0.01,
bufferingGoal: 0.5,
bufferBehind: 2,
retryParameters: {
baseDelay: 100,
},
},
});
await player.load("https://example.com/live/low-latency.m3u8");
Low-latency mode works with both LL-HLS (partial segments, preload hints, delta updates) and LL-DASH. The aggressive buffer settings keep latency tight while the retry parameters handle the inevitable hiccups of live delivery.
That's a Wrap
Shaka Player is one of those libraries that earns its place by doing many things well instead of one thing perfectly. It handles DASH and HLS with the same API, supports every major DRM system, provides offline storage out of the box, and runs on everything from desktop Chrome to Tizen smart TVs. The v5 release cleans up the API surface and drops legacy baggage, making it a solid time to start a new project with it.
The tradeoff is bundle size — if you only need HLS, hls.js is lighter. If you only need DASH, dash.js is more focused. But if your requirements include multiple formats, DRM, offline downloads, or ad insertion, reaching for separate libraries and gluing them together will cost you more than the extra kilobytes. shaka-player is the one player that genuinely tries to do it all, and mostly succeeds.