Skip to main content

🎬 Audio and Video Implementation

HTML5 lets you embed sound and moving pictures natively β€” no plugins, no Flash. This lesson covers the <audio> and <video> elements from first principles: multiple sources for cross-browser support, the attributes that shape playback, responsive video, accessible captions, and scripting playback with the media API.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Embed sound and video with the <audio> and <video> elements
  • Provide multiple <source> formats and a text fallback for compatibility
  • Control playback with attributes like controls, preload, poster, muted, and loop
  • Make video responsive and add captions with the <track> element and WebVTT
  • Script playback with the JavaScript media API (play, pause, events)

Estimated Time: 35–45 minutes  β€’  Difficulty: Beginner–Intermediate

Hands-on: Build an accessible, captioned, responsive video player and wire up a custom play/pause button.

In This Lesson

Native Media in HTML5

Before HTML5, playing a video on the web meant embedding a proprietary plugin like Flash β€” a source of endless security holes and crashes. HTML5 swept that away with two simple elements, <audio> and <video>, that every modern browser understands natively. They come with built-in controls, a rich set of attributes, and a JavaScript API for full control.

Both elements share the same core design: an outer tag, one or more <source> children offering the file in different formats, and fallback content for browsers that somehow can't play any of them.

graph TD A[HTML5 Media] --> B[audio element] A --> C[video element] B --> B1[source: mp3, ogg] C --> C1[source: mp4, webm] C --> C2[track: captions] A --> D[Media JavaScript API]

The audio Element

The simplest audio player is one line β€” but always include controls so the user can actually play it:

<audio src="/media/theme.mp3" controls></audio>

Unlike <img>, the audio element needs a closing tag because it can hold <source> children and fallback text. Offer more than one format so every browser finds one it supports, and put a helpful message (and a download link) inside as a last resort:

<audio controls preload="metadata">
  <source src="/media/episode.mp3" type="audio/mpeg">
  <source src="/media/episode.ogg" type="audio/ogg">
  <p>Your browser can't play this audio.
     <a href="/media/episode.mp3">Download it</a> instead.</p>
</audio>
Structure of an audio element A diagram of the audio element containing two source elements, a primary MP3 and a fallback OGG, plus fallback text. <audio controls> <source src="episode.mp3" type="audio/mpeg"> β€” primary <source src="episode.ogg" type="audio/ogg"> β€” fallback </audio>
Figure 1 β€” The browser tries each <source> top to bottom and plays the first format it supports, falling back to the text inside if none work.

Key audio attributes

AttributeEffect
controlsShows the browser's play/pause/volume UI (almost always include this)
preloadnone, metadata, or auto β€” how much to load before play
autoplayStarts on load β€” avoid with sound; browsers block it anyway
loopRepeats indefinitely
mutedStarts silent

πŸ’‘ Pick a sensible preload

Use preload="none" for background music the user might never play, and preload="metadata" (the safe default) for a podcast so the duration shows without downloading the whole file. Reserve preload="auto" for media you're confident the user will play.

The video Element

Video works exactly like audio, with a few extra attributes for the visual dimension. Set width and height (or use CSS) and always add a poster image to show before playback:

<video controls width="640" height="360"
       poster="/media/intro-poster.jpg" preload="metadata" playsinline>
  <source src="/media/intro.mp4" type="video/mp4">
  <source src="/media/intro.webm" type="video/webm">
  <track src="/media/intro-en.vtt" kind="captions" srclang="en" label="English" default>
  <p>Your browser can't play this video.
     <a href="/media/intro.mp4">Download it</a>.</p>
</video>
AttributeEffect
posterImage shown before the video plays β€” set one for a polished look
mutedRequired for autoplay to be allowed by modern browsers
playsinlinePlays inline on iOS instead of forcing fullscreen
width/heightReserve space and set aspect ratio; prevents layout shift

⚠️ The autoplay rule

Browsers only allow a video to autoplay if it is also muted (and often playsinline). This is deliberate β€” nobody wants a page blasting sound unexpectedly. For a decorative background video, combine autoplay muted loop playsinline.

Formats & Codecs

A media file is a container (like MP4 or WebM) holding streams compressed by codecs (like H.264 for video or AAC for audio). Browser support varies, which is why offering two formats is the safe strategy.

TypeFormatMIME typeWhen to use
AudioMP3audio/mpegUniversal support β€” your primary source
OGG / Opusaudio/oggOpen format, good fallback
VideoMP4 (H.264/AAC)video/mp4Universal support β€” your primary source
WebM (VP9/Opus)video/webmSmaller files where supported

For maximum compatibility, provide MP3 + OGG for audio and MP4 + WebM for video:

<video controls width="640" height="360">
  <source src="/media/demo.webm" type="video/webm">  <!-- smaller, tried first -->
  <source src="/media/demo.mp4"  type="video/mp4">   <!-- universal fallback -->
</video>
flowchart LR A[Media file] --> B[Container: MP4 / WebM] A --> C[Video codec: H.264 / VP9 / AV1] A --> D[Audio codec: AAC / Opus]

βœ… Ordering tip

List the smaller, more efficient format (WebM/AVIF-class) first and the universal MP4 second. The browser plays the first source it can, so capable browsers get the lighter file automatically.

Responsive Video

A fixed width="640" overflows a phone screen. The quickest fix is a touch of CSS:

video {
  max-width: 100%;
  height: auto;
}

To hold a video (or an <iframe> embed) at a fixed aspect ratio while it scales, the modern approach is the aspect-ratio property β€” now well supported:

.video-wrapper {
  aspect-ratio: 16 / 9;
  max-width: 100%;
}
.video-wrapper video,
.video-wrapper iframe {
  width: 100%;
  height: 100%;
}

For embeds from platforms like YouTube, wrap the <iframe> in that same wrapper. Prefer the privacy-friendly domain and let the user opt in:

<div class="video-wrapper">
  <iframe
    src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
    title="Product demo video"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
    allowfullscreen></iframe>
</div>

πŸ’‘ Older-browser fallback

Before aspect-ratio, developers faked ratios with the "padding-bottom" trick: a wrapper with padding-bottom: 56.25% (that's 9 Γ· 16) and an absolutely positioned child. You'll still see it in older codebases β€” but reach for aspect-ratio in new work.

Captions & Accessibility

Media that can't be perceived by everyone excludes people β€” and in many places it's a legal requirement to caption it. Accessibility here is mostly about giving text equivalents for sound and visuals.

Captions with the track element

The <track> element attaches a caption or subtitle file (in WebVTT format) to a video. Mark one as default to show it automatically:

<video controls width="640" height="360">
  <source src="/media/lesson.mp4" type="video/mp4">
  <track src="/media/lesson-en.vtt" kind="captions" srclang="en" label="English" default>
  <track src="/media/lesson-es.vtt" kind="subtitles" srclang="es" label="EspaΓ±ol">
</video>

WebVTT β€” the caption file format

A WebVTT file starts with WEBVTT and lists time-stamped cues:

WEBVTT

00:00:01.000 --> 00:00:04.000
Welcome to our tutorial on HTML video.

00:00:05.000 --> 00:00:09.000
In this clip we'll add captions to a video element.

Transcripts for audio

Audio has no <track>, so provide a transcript β€” a collapsible <details> block is a tidy pattern:

<figure>
  <audio controls>
    <source src="/media/episode.mp3" type="audio/mpeg">
  </audio>
  <figcaption>Episode 42 β€” Interview with Jane Smith</figcaption>
</figure>

<details>
  <summary>View transcript</summary>
  <p><strong>Host:</strong> Welcome to the show…</p>
</details>

βœ… Accessibility checklist

  • Add captions (kind="captions") to every video with speech.
  • Provide a transcript for audio-only content.
  • Never autoplay with sound β€” it disrupts screen-reader users.
  • Keep controls keyboard-accessible (the native controls UI already is).
  • Use kind="descriptions" tracks to narrate key visuals for blind users.

The Media JavaScript API

Every <audio> and <video> element exposes methods, properties, and events so you can build custom controls or react to playback. The essentials: .play(), .pause(), .currentTime, .volume, and .muted.

<video id="player" width="640" height="360">
  <source src="/media/demo.mp4" type="video/mp4">
</video>
<button id="toggle" type="button">Play</button>
const video = document.getElementById('player');
const toggle = document.getElementById('toggle');

// A single play/pause button that stays in sync with the video
toggle.addEventListener('click', () => {
  if (video.paused) {
    video.play();
  } else {
    video.pause();
  }
});

// Keep the label correct even if the user uses native controls
video.addEventListener('play',  () => { toggle.textContent = 'Pause'; });
video.addEventListener('pause', () => { toggle.textContent = 'Play'; });
video.addEventListener('ended', () => { toggle.textContent = 'Replay'; });

Media elements fire many events β€” loadedmetadata, canplay, timeupdate, ended, error β€” that let you build progress bars, analytics, or playlists. Here's a progress readout driven by timeupdate:

video.addEventListener('timeupdate', () => {
  const pct = (video.currentTime / video.duration) * 100;
  console.log(`Watched ${pct.toFixed(0)}%`);
});

πŸ’‘ Prefer native controls first

The browser's built-in controls are already accessible and keyboard-friendly. Only build custom controls when design demands it β€” and then re-implement keyboard support, focus states, and ARIA labels you'd otherwise get for free.

Hands-on Exercise

πŸ‹οΈ Build an Accessible Captioned Player

Objective: Assemble a responsive video with two source formats, a poster, captions, and a custom play/pause button.

Instructions:

  1. Create a <video> with controls, a poster, and both an MP4 and a WebM <source>.
  2. Add an English <track kind="captions" default> pointing at a .vtt file.
  3. Wrap it in a .video-wrapper using aspect-ratio: 16 / 9 so it scales.
  4. Add a button and a few lines of JS that toggle play/pause and keep the label in sync.
  5. Write a two-cue WebVTT file to prove the captions appear.
πŸ’‘ Hint

Check video.paused inside the click handler to decide between .play() and .pause(). Listen for the video's own play and pause events to update the button text, so it stays correct even when the native controls are used.

βœ… Sample solution
<div class="video-wrapper">
  <video id="player" controls poster="/media/poster.jpg" preload="metadata" playsinline>
    <source src="/media/clip.webm" type="video/webm">
    <source src="/media/clip.mp4"  type="video/mp4">
    <track src="/media/clip-en.vtt" kind="captions" srclang="en" label="English" default>
  </video>
</div>
<button id="toggle" type="button">Play</button>
.video-wrapper { aspect-ratio: 16 / 9; max-width: 100%; }
.video-wrapper video { width: 100%; height: 100%; }
const v = document.getElementById('player');
const b = document.getElementById('toggle');
b.addEventListener('click', () => v.paused ? v.play() : v.pause());
v.addEventListener('play',  () => b.textContent = 'Pause');
v.addEventListener('pause', () => b.textContent = 'Play');
WEBVTT

00:00:00.500 --> 00:00:03.000
Hello β€” this clip has captions.

00:00:03.500 --> 00:00:06.000
And they were added with the track element.

Summary & Quiz

πŸŽ‰ Key Takeaways

  • <audio> and <video> embed media natively with built-in controls and fallback content.
  • Offer multiple <source> formats (MP3+OGG, MP4+WebM) for cross-browser support.
  • Autoplay is only allowed when the media is muted; always give video a poster.
  • Make video responsive with aspect-ratio, and caption it with <track> + WebVTT.
  • The media API (play(), pause(), events) powers custom controls and interactivity.

🎯 Quick Quiz

Question 1: Why provide more than one <source> inside a <video> element?

Question 2: A modern browser will let a video autoplay only if it is also…

Question 3: Which element attaches a WebVTT caption file to a video?

πŸ“š Further Reading

πŸš€ What's Next?

You've now covered text, links, images, and media β€” the full range of content elements. Next we turn to organizing tabular data cleanly and accessibly with table structure and semantics.

πŸŽ‰ Excellent!

Your pages can now speak and move β€” accessibly, responsively, and without a single plugin.