Capabilities · API
Media Session API
Published Updated
In one line: the Media Session API “provides a way to customize media notifications,” per MDN — it lets a page describe what is playing and receive events from a device’s physical or onscreen media controls, alongside whatever on-page playback controls the page already has.
What it is
Section titled “What it is”The entry point is navigator.mediaSession, a MediaSession object. Per MDN’s
setActionHandler() reference, its actions “let a web app receive notifications when
the user engages a device’s built-in physical or onscreen media controls, such as play,
stop, or seek buttons,” and per MDN’s metadata reference, MediaSession.metadata holds
a MediaMetadata object that provides descriptive information about the currently
playing media for the device’s own media control UI.
Where it is supported
Section titled “Where it is supported”Per MDN’s browser-compat-data, the MediaSession interface (and its setActionHandler()
method) is supported in Chrome from version 73, in Safari from version 15, and in Firefox
from version 82. Chrome for Android has supported it from version 57, but Firefox for
Android is a partial implementation — the same data notes that Firefox for Android
“exposes the API, but does not provide a corresponding user-facing media control
interface.” The data records Android WebView as unsupported (version_added: false).
How to use it
Section titled “How to use it”Set metadata with a MediaMetadata object, then register handlers for the actions the
page can respond to:
const playlist = [ { title: "Unforgettable", artist: "Nat King Cole", album: "The Ultimate Collection (Remastered)" }, { title: "L-O-V-E", artist: "Nat King Cole", album: "The Ultimate Collection (Remastered)" },];let currentTrackIndex = 0;const audioElement = document.querySelector("audio") ?? document.createElement("audio");
function loadTrack(index) { currentTrackIndex = index; const track = playlist[currentTrackIndex]; audioElement.src = `/audio/${track.title}.mp3`; navigator.mediaSession.metadata = new MediaMetadata({ title: track.title, artist: track.artist, album: track.album, artwork: [{ src: "/art/unforgettable-96.png", sizes: "96x96", type: "image/png" }], });}
function playPreviousTrack() { if (currentTrackIndex > 0) { loadTrack(currentTrackIndex - 1); audioElement.play(); }}
function playNextTrack() { if (currentTrackIndex < playlist.length - 1) { loadTrack(currentTrackIndex + 1); audioElement.play(); }}
loadTrack(0);
const actionHandlers = { play: () => audioElement.play(), pause: () => audioElement.pause(), previoustrack: playPreviousTrack, nexttrack: playNextTrack,};
for (const [action, handler] of Object.entries(actionHandlers)) { try { navigator.mediaSession.setActionHandler(action, handler); } catch { // This browser doesn't implement the "action" media session action — skip it. }}Per MDN’s setActionHandler() reference, passing null as the callback removes a
previously-set handler for that action, e.g.
navigator.mediaSession.setActionHandler("nexttrack", null).
How to detect it at runtime
Section titled “How to detect it at runtime”Feature-detect mediaSession on navigator before reading or writing it, and fall back
to showing the page’s own on-page playback controls as the only control surface. This
example creates that fallback element itself (a native <audio controls>) so it works
when pasted as-is, instead of assuming markup that was never added to the page:
function supportsMediaSession() { return "mediaSession" in navigator;}
const playerControls = document.querySelector("#player-controls") ?? createPlayerControls();
if (supportsMediaSession()) { navigator.mediaSession.metadata = new MediaMetadata({ title: "Unforgettable" });} else { // No Media Session support here — rely on the page's own play/pause/seek // controls instead of OS-level media controls. showOnPageTransportControls();}
function createPlayerControls() { const audio = document.createElement("audio"); audio.id = "player-controls"; audio.controls = true; audio.src = "/audio/unforgettable.mp3"; audio.hidden = true; document.body.appendChild(audio); return audio;}
function showOnPageTransportControls() { playerControls.hidden = false;}Practical checklist
Section titled “Practical checklist”- Per MDN’s compat data, Firefox for Android “exposes the API, but does not provide a
corresponding user-facing media control interface” — do not assume a set handler is
reachable by the user on that platform just because
setActionHandler()did not throw. - Android WebView is recorded as unsupported in the same data — an app embedded in a WebView still needs the on-page fallback controls.
- Per MDN’s
setActionHandler()reference, support for individual action strings (e.g.skipad,previousslide,nextslide) varies by browser, and calling it with an action a given browser doesn’t implement can throw — MDN’s own examples wrap eachsetActionHandler()call in its owntry...catchso one unsupported action doesn’t break the rest of the session setup. - Per MDN’s
metadatareference,MediaSession.metadataisnulluntil the page sets it — read it defensively rather than assuming aMediaMetadataobject is already present.
Where to go next
Section titled “Where to go next”Specifications
| Specification | Status |
|---|---|
| None. | |