Not long ago, playing a video on a web page needed a browser plug-in. Today HTML audio and video work out of the box: two built-in elements, <audio> and <video>, give you a working media player with a single line of markup. In this lesson you will learn how to use them, which file formats to provide, and how to make media accessible. It continues from HTML Semantic Elements and uses the same path rules as HTML Images.
The audio element
The simplest audio player looks like this: <audio src="sounds/temple-bells.mp3" controls></audio>. The controls attribute is a boolean attribute that shows the browser’s play button, timeline and volume control. Without it, the audio element is invisible and can only be played by JavaScript.
The Try it editor cannot load sound files, so the example below uses a short script to create a one-second tone and give it to the player. You do not need to understand the script yet – focus on the <audio> element.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Audio player</title>
</head>
<body>
<h1>Notification sound</h1>
<audio id="player" controls>
Your browser does not support the audio element.
</audio>
<p>Press play to hear a one-second tone.</p>
<script>
// Builds a tiny WAV file in memory so this demo needs no external file.
const rate = 8000, samples = rate;
const bytes = new Uint8Array(44 + samples * 2);
const view = new DataView(bytes.buffer);
const text = (pos, str) => [...str].forEach((c, i) => view.setUint8(pos + i, c.charCodeAt(0)));
text(0, 'RIFF'); view.setUint32(4, 36 + samples * 2, true); text(8, 'WAVEfmt ');
view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true);
view.setUint32(24, rate, true); view.setUint32(28, rate * 2, true);
view.setUint16(32, 2, true); view.setUint16(34, 16, true);
text(36, 'data'); view.setUint32(40, samples * 2, true);
for (let i = 0; i < samples; i++) {
const fade = 1 - i / samples;
view.setInt16(44 + i * 2, Math.sin(2 * Math.PI * 440 * i / rate) * 8000 * fade, true);
}
let binary = '';
bytes.forEach(b => binary += String.fromCharCode(b));
document.getElementById('player').src = 'data:audio/wav;base64,' + btoa(binary);
</script>
</body>
</html>Any text placed between the opening and closing tags is fallback content. It is only shown by very old browsers that do not understand the element, so keep it short – a sentence and perhaps a download link.
The video element
Video works the same way, with a few extra attributes:
| Attribute | What it does |
|---|---|
controls | Shows the built-in player controls. Include it unless you build your own. |
width / height | Reserve space, like with images, to avoid layout shift. |
poster | An image shown before the video starts playing. |
preload | A hint: none, metadata (just duration and size) or auto. metadata is a sensible choice for most pages. |
loop | Starts again from the beginning when the video ends. |
muted | Starts with the sound off. |
autoplay | Tries to start playing as soon as possible – see the warning below. |
playsinline | Plays inside the page on iPhones instead of switching to full screen. |
Offering HTML audio and video in more than one format
Instead of a single src, you can list several <source> elements. The browser checks them in order and plays the first one it supports. The type attribute lets it skip unsupported files without downloading them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Video player</title>
<style>
video { max-width: 100%; height: auto; background: #000; }
</style>
</head>
<body>
<h1>Train journey</h1>
<video controls width="480" height="270" preload="metadata"
poster="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='480'%20height='270'%3E%3Crect%20width='100%25'%20height='100%25'%20fill='%231d3557'/%3E%3Ctext%20x='50%25'%20y='52%25'%20font-family='sans-serif'%20font-size='26'%20text-anchor='middle'%20fill='white'%3EPoster:%20Chennai%20Central%3C/text%3E%3C/svg%3E">
<source src="media/chennai-central.webm" type="video/webm">
<source src="media/chennai-central.mp4" type="video/mp4">
<p>Your browser cannot play this video.
<a href="media/chennai-central.mp4">Download the video</a> instead.</p>
</video>
<p>There is no real video file in this editor, so you only see the poster image.
On your own site, point the sources at your video files.</p>
</body>
</html>| Format | Notes |
|---|---|
| MP4 (H.264 video, AAC audio) | Plays in every modern browser – the safest single choice. |
| WebM (VP9 or AV1 video, Opus audio) | Open formats, often smaller files; list it first if you have it. |
| MP3 | Universal choice for audio. |
| AAC / Opus | Efficient audio formats with wide modern support. |
Autoplay: use it carefully
Sound that starts on its own is one of the most disliked things on the web, and it is a real accessibility problem: it can drown out a screen reader. For this reason, browsers block autoplay with sound until the user has interacted with the site. Autoplay is generally allowed only when the media is muted.
Watch out: If you need a silent background video, use autoplay muted loop playsinline together and keep controls or another visible way to pause it. Moving content that plays for more than five seconds must be pausable to meet WCAG.
Captions, subtitles and transcripts
People who are deaf or hard of hearing, people watching in a noisy train, and people learning a language all rely on captions. Add them with the <track> element, which points to a WebVTT (.vtt) text file containing timed text.
<video controls width="640" height="360">
<source src="media/kolam-tutorial.mp4" type="video/mp4">
<track src="media/kolam-tutorial.en.vtt" kind="captions"
srclang="en" label="English" default>
<track src="media/kolam-tutorial.ta.vtt" kind="subtitles"
srclang="ta" label="தமிழ்">
</video>A WebVTT file is plain text. It starts with the word WEBVTT, followed by cues: a time range such as 00:00:01.000 --> 00:00:04.000 and the text to show. Use kind="captions" when the text includes sounds as well as speech, and kind="subtitles" for translations. Note that track files must come from the same site as the page unless the server allows cross-origin requests.
For audio-only content such as podcasts, provide a written transcript on the page. Transcripts help users who cannot hear, and they also give search engines text to index.
Embedding videos from other sites
Video hosting services usually give you an <iframe> embed code. An iframe displays another web page inside yours. Always add a title attribute describing the content, such as title="Video: How to draw a kolam", so screen reader users know what the frame contains, and consider loading="lazy" for embeds further down the page.
HTML audio and video players can also be controlled with JavaScript, for example to build custom play buttons – the techniques are in JavaScript Events. In the final lesson of this track, HTML Head, Meta Tags and SEO, you will return to the <head> and prepare your pages for search engines and social sharing.
Further reading
- developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/video
- developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/audio
- developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/track
- developer.mozilla.org/en-US/docs/Web/API/WebVTT_API
- developer.mozilla.org/en-US/docs/Web/Media/Guides/Autoplay
- html.spec.whatwg.org/multipage/media.html
- w3.org/WAI/media/av
