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 MediaRecorder and treats <video> only in passing alongside <audio> — so those sections are written from general/official (MDN) knowledge instead, as their headings note again below.

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

play()

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.

pause()

Pauses playback in place; calling play() again resumes from the same position.

currentTime

The current playback position, in seconds. Reading it reports progress; writing it seeks — setting audio.currentTime = 0 restarts playback from the beginning.

volume

A number from 0.0 (silent) to 1.0 (full volume) controlling the audio output level.

playbackRate

A multiplier on normal playback speed — 2.0 plays at double speed, 0.5 at half speed.

paused / ended

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

loadedmetadata

The element has finished loading enough of the media to know its duration and dimensions (for video). A good point to read audio.duration for the first time.

timeupdate

The playback position (currentTime) has changed — fires repeatedly during playback, which makes it the usual way to drive a progress bar.

play / pause

Playback has started or been paused, whether triggered by script or by the user through native controls.

ended

Playback has reached the end of the media and stopped. Does not fire if the element loops (a loop attribute or property restarts playback instead).

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

videoWidth / videoHeight

The intrinsic pixel dimensions of the video’s decoded frames — not the element’s rendered CSS size, which is read with getBoundingClientRect() instead (see Document Geometry & Scrolling).

poster

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}`);