Audio & Video APIs
|
This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve. This section’s bibliography lists the reference material consulted while preparing these pages. |
The <audio> and <video> elements let a page embed and control media playback without any plugin, and the
browser also exposes lower-level APIs for capturing media from a user’s microphone or camera. This page covers
controlling playback through HTMLAudioElement/HTMLVideoElement, recording audio with MediaDevices and
MediaRecorder, and the video-specific pieces of the platform — <track> captions and the properties unique to
HTMLVideoElement.
|
Coverage depth varies by section here. Audio Playback below is grounded in Flanagan §15.9 ("Audio APIs").
Audio Recording and the entire Video APIs section have no dedicated coverage in the book — Flanagan’s
chapter on browser JavaScript predates |
Audio Playback
Every <audio> element in a page — whether written in HTML markup or created dynamically — is represented in
JavaScript by an HTMLAudioElement object. You don’t even need markup to play a sound: the Audio()
constructor is a shortcut for document.createElement("audio") that creates an element you can play without
ever inserting it into the document:
// Load the sound effect in advance so it is ready for use
let soundeffect = new Audio("soundeffect.mp3");
// Play the sound effect whenever the user clicks the mouse button
document.addEventListener("click", () => {
soundeffect.cloneNode().play(); // load and play a fresh copy of the sound
});
cloneNode() matters here: if the user clicks rapidly, playing the same Audio object again would just
restart it, cutting off the previous playback. Cloning creates a new, independent element so overlapping copies
of the sound can play at once. Because the clones are never added to the document, they are simply garbage
collected once they finish playing.
Controlling Playback
HTMLAudioElement (and, as covered later, HTMLVideoElement) exposes the same small set of methods and
properties for controlling playback, regardless of how the element was created:
| Member | What it does |
|---|---|
|
Starts (or resumes) playback. Returns a promise that resolves once playback actually begins — browsers may reject it if autoplay is blocked, so it’s worth handling the rejection. |
|
Pauses playback in place; calling |
|
The current playback position, in seconds. Reading it reports progress; writing it seeks — setting
|
|
A number from |
|
A multiplier on normal playback speed — |
|
Read-only booleans reporting whether playback is currently paused, or has run to the end of the media. |
let audio = document.querySelector("#chime");
audio.volume = 0.5; // half volume
audio.currentTime = 10; // seek to the 10-second mark
audio.play(); // begin playback -- returns a promise
Common Events
HTMLAudioElement fires events throughout the media’s lifecycle, which is how code reacts to playback state
without polling currentTime or paused in a loop:
| Event | Fires when |
|---|---|
|
The element has finished loading enough of the media to know its |
|
The playback position ( |
|
Playback has started or been paused, whether triggered by script or by the user through native controls. |
|
Playback has reached the end of the media and stopped. Does not fire if the element loops (a |
let audio = document.querySelector("#chime");
audio.addEventListener("loadedmetadata", () => {
console.log(`duration: ${audio.duration}s`);
});
audio.addEventListener("timeupdate", () => {
progressBar.value = audio.currentTime / audio.duration;
});
audio.addEventListener("ended", () => {
console.log("playback finished");
});
Audio Recording (General Knowledge)
Recording audio from a microphone is not covered by the book — the APIs below come from general/official (MDN) documentation.
Requesting Microphone Access
navigator.mediaDevices.getUserMedia() asks the browser to grant the page access to a media input device. It
returns a promise that resolves with a MediaStream once the user grants permission, and rejects if they deny
it or no matching device exists:
async function requestMicrophone() {
try {
// The browser shows a permission prompt the first time a page calls this
let stream = await navigator.mediaDevices.getUserMedia({ audio: true });
return stream;
} catch (err) {
// NotAllowedError (user or system denied permission), NotFoundError (no
// microphone), etc.
console.error("Microphone access denied:", err);
}
}
The permission prompt is browser-controlled UI that a page cannot suppress or bypass — it’s shown the first
time (per origin) a page requests a given device, and the user’s choice is typically remembered for future
visits. getUserMedia() also requires a secure context (HTTPS, or localhost during development); it simply
isn’t available over plain HTTP.
Recording with MediaRecorder
Once a MediaStream is in hand, MediaRecorder captures it into a sequence of Blob chunks. Recording is
started and stopped explicitly, and the recorded data arrives asynchronously through the dataavailable event
rather than as a return value:
async function recordFor(milliseconds) {
let stream = await navigator.mediaDevices.getUserMedia({ audio: true });
let recorder = new MediaRecorder(stream);
let chunks = [];
// Each time the recorder has data ready, it's delivered here as a Blob
recorder.addEventListener("dataavailable", (event) => {
chunks.push(event.data);
});
return new Promise((resolve) => {
recorder.addEventListener("stop", () => {
// Assemble all the chunks into a single playable Blob
let audioBlob = new Blob(chunks, { type: "audio/webm" });
resolve(audioBlob);
// Release the microphone once recording is done
stream.getTracks().forEach((track) => track.stop());
});
recorder.start();
setTimeout(() => recorder.stop(), milliseconds);
});
}
The resulting Blob can be played back by pointing an <audio> element at it with URL.createObjectURL(),
uploaded with fetch() (see Networking), or handed to the File
System Access / download APIs to save locally. MediaRecorder can also call start(timeslice) with a
millisecond interval to fire dataavailable periodically during a long recording instead of only once at the
end.
Video APIs (General Knowledge)
Like audio recording, <video>-specific behavior has no dedicated coverage in the book and is written from
general/official (MDN) knowledge.
HTMLVideoElement Playback Control
HTMLVideoElement inherits from the same HTMLMediaElement interface as HTMLAudioElement, so everything
covered above under Controlling Playback and Common Events — play(), pause(), currentTime, volume, playbackRate, and the
loadedmetadata/timeupdate/play/pause/ended events — applies unchanged to <video>:
let video = document.querySelector("#clip");
video.volume = 0.8;
video.playbackRate = 1.5;
video.addEventListener("ended", () => console.log("clip finished"));
video.play();
Captions and Subtitles with <track>
A <track> element, nested inside <video>, associates a timed text file (WebVTT) with the video for captions,
subtitles, descriptions, or chapters. kind selects the track’s purpose, srclang its language, and label
the text shown in the video player’s track-selection menu:
let track = document.createElement("track");
track.kind = "captions"; // also: "subtitles", "descriptions", "chapters", "metadata"
track.srclang = "en";
track.label = "English";
track.src = "captions-en.vtt";
video.appendChild(track);
Multiple <track> elements can be attached for different languages or purposes at once; the browser’s native
controls (or custom UI built on video.textTracks) let the viewer pick among them.
Video-Specific Properties
A handful of properties exist only on HTMLVideoElement, since they describe the visual frame rather than
anything audio playback needs:
| Property | What it reports |
|---|---|
|
The intrinsic pixel dimensions of the video’s decoded frames — not the element’s rendered CSS size, which is
read with |
|
The URL of an image shown in place of the first frame before playback starts (or while the video is loading). |
video.poster = "thumbnail.jpg";
console.log(`native size: ${video.videoWidth}x${video.videoHeight}`);