Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~27 min· topic 3 of 7

Topic 5.3

Audio and Video

In one line

<audio> and <video> play sound and film right in the page; attributes control the player, <source> offers formats in order, and browsers only autoplay when the sound is off.

Think of it like this

A TV in a shop window. It can play on its own only with the sound off, otherwise it would annoy everyone walking past. If you want sound, you walk in and press play yourself. Browsers follow the same rule: video may start by itself only when it is muted.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Media element
<audio> or <video>: an element the browser can play, with a built-in player.
Boolean attribute
An attribute that is on when written and off when left out, like muted or controls.
Autoplay
Starting playback without the user pressing play.
User gesture
A real action by the person, such as a click, tap or key press.
Poster
A still image shown in a video player before the video starts.
MIME type
A standard name for a file format, like video/mp4 or audio/mpeg.
Codec
The method used to compress video or audio (H.264, VP9, AV1, AAC, Opus). A browser must support it to play the file.
Fallback content
Content inside an element that is shown only by browsers that don't support the element.
Promise
A JavaScript object for a result that arrives later: it either succeeds or fails (is rejected).

Step by step

01The smallest player

One element and one attribute. Without controls, a user would see the first frame and have no way to start it.

index.htmlwhole filehtml
<video src="intro.mp4" controls width="640" height="360"></video>

02Add a poster and choose preload

The poster appears until the video starts, so the box isn't black. preload="metadata" fetches only enough to show the length on the timeline.

index.htmlwhole filehtml
<video src="intro.mp4" controls width="640" height="360"
       poster="intro-poster.jpg" preload="metadata"></video>

03Offer formats in order

Put the smallest modern format first and the one that plays everywhere last. The browser checks type and skips what it can't play, so it never downloads a file it can't use.

The text at the end is for very old browsers and also good practice: it gives a download link.

index.htmlwhole filehtml
<video controls width="640" height="360" poster="intro-poster.jpg" preload="metadata">
  <source src="intro.av1.mp4" type='video/mp4; codecs="av01.0.05M.08"'>
  <source src="intro.webm" type="video/webm">
  <source src="intro.mp4" type="video/mp4">
  <p>Your browser can't play this video. <a href="intro.mp4">Download it</a>.</p>
</video>
Offer formats in orderdiagram
Rendering diagram…

04A silent background loop

For a decorative loop (no speech, no information), all four attributes together: it starts at once on every browser because it is muted, repeats forever and stays inline on iPhones.

Remember people who are sensitive to motion: give them a way to pause it, and consider the prefers-reduced-motion media query (CSS course).

index.htmlwhole filehtml
<video src="waves.mp4" autoplay muted loop playsinline
       width="1280" height="720" aria-hidden="true"></video>

05Start with sound from a click

Playback with sound must come from a user gesture. Call play() inside a click handler and handle the promise, because playback can still fail (for example, the file is missing).

app.jswhole filejavascript
const video = document.querySelector("video");
document.querySelector("#play").addEventListener("click", async () => {
  try {
    video.muted = false;
    await video.play();
  } catch (err) {
    console.log("Could not play:", err.name); // NotAllowedError or NotSupportedError
  }
});

06Audio works the same way

<audio> has the same attributes (except poster, width, height and playsinline). Without controls it is invisible, so keep controls unless you build your own buttons.

index.htmlwhole filehtml
<audio controls preload="none">
  <source src="episode-12.opus" type="audio/ogg; codecs=opus">
  <source src="episode-12.mp3" type="audio/mpeg">
  <a href="episode-12.mp3">Download episode 12 (MP3)</a>
</audio>

Try it yourself

  1. 1

    Unmute the first example

    Predict: if you remove muted from the first video, will it still start by itself? The stream has no sound track, but the browser judges by the muted flag. Run it and read the line under the player.

  2. 2

    Press play yourself

    In the audio example, if the line says "Blocked", press the player's play button. It works now: your click is a user gesture.

  3. 3

    Remove controls

    Delete controls from the first video. The picture still moves (it is muted autoplay), but there's no way to pause it. That's why silent loops still need a pause control for accessibility.

Code & diagrams

A real video player (the video is drawn by JavaScript) New tab

There's no video file here: JavaScript paints a canvas and streams it into the player. Use the controls to pause, and try the full-screen button.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

muted: true | loop: true | paused: true
readyState before any media: 0
Audio: a tone made by JavaScript, and the autoplay rule New tab

The page tries to play sound without a click. Most browsers block it (NotAllowedError); pressing the play button is a user gesture, so that always works.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Duration in seconds: 1
When no source can play New tab

`canPlayType` answers "" (no), "maybe" or "probably". Unknown types are skipped without downloading, and the fallback text stays hidden because the browser does support video.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Can play video/x-future-one? ""
Skipped source: video/x-future-one
Skipped source: video/x-future-two
networkState: 3 (NETWORK_NO_SOURCE)
Fallback paragraph visible? false

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Autoplay with sound

Write <video src="promo.mp4" autoplay></video> (no muted) and call video.play() on page load.

terminal
$ Console
── what you'll see ──
Uncaught (in promise) NotAllowedError: play() failed because the user didn't interact with the document first.

Break #2

Wrong MIME type on the source

List an MP4 file as <source src="intro.mp4" type="video/mpeg4"> (a made-up type).

terminal
$ the page shows
── what you'll see ──
An empty player with a crossed-out play button; the file is never requested in the Network panel.

Myth vs fact

Myth

Fallback text inside <video> shows when the file can't play.

Fact

It shows only in browsers that don't support <video> at all. A failed file fires an error event; you must handle that yourself.

Myth

autoplay makes a video play.

Fact

It is a request. Browsers ignore it for unmuted media until the user interacts with the page, and data-saver modes may ignore it entirely.

Myth

preload="auto" downloads the whole video.

Fact

It is a hint. The browser decides how much to buffer, and mobile browsers often load little or nothing until play.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Chrome's autoplay policy allows unmuted autoplay after a user gesture on the page or when the site's Media Engagement Index (how much this user has watched media there) is high enough. Cross-origin iframes also need allow="autoplay" from the embedding page (Topic 5.5).

  • ▸

    For long videos, sites use adaptive streaming (HLS or MPEG-DASH): the video is cut into small chunks at several qualities, and JavaScript (Media Source Extensions) switches quality as bandwidth changes. A single MP4 can't adapt.

  • ▸

    Servers must support HTTP range requests (Accept-Ranges: bytes, status 206 Partial Content) so the browser can seek without downloading everything; it also lets preload="metadata" fetch only the header. (See the System Design course for how CDNs serve this.)

  • ▸

    HTMLMediaElement.canPlayType() returns "probably", "maybe" or ""; the vague answers exist because a container type alone (like video/mp4) doesn't say which codecs are inside.

Remember this

  1. 1

    <video> and <audio> are media elements: the browser downloads the file, decodes it and plays it with its own built-in player. You don't need plugins (the old Flash player is gone). The simplest form is <video src="talk.mp4" controls></video>. The **controls** attribute shows the browser's play button, timeline, volume and full-screen buttons; without it the player has no buttons at all, and only JavaScript can start it.

  2. 2

    Other on/off attributes (called boolean attributes: present means on, absent means off): **muted starts with the sound off, loop starts again at the end, autoplay asks to start playing as soon as possible, and playsinline asks iPhones to play inside the page instead of jumping to full screen. poster** (video only) is an image shown before playback starts, like a cover. Always give a video width and height too, for the same layout-shift reason as images (Topic 5.2).

  3. 3

    Autoplay rules. Browsers block videos that start with sound by themselves. Muted autoplay is allowed everywhere. Unmuted playback needs a user gesture (a click or tap on the page) or, in Chrome, a history of the user watching media on that site. When blocked, JavaScript's video.play() returns a promise that is rejected with a NotAllowedError. So: autoplay muted playsinline for silent background loops; a play button for anything with sound.

  4. 4

    **<source> fallbacks.** Instead of one src, you can list several <source src="…" type="…"> children. The browser goes down the list and uses the **first one whose type it can play**, without downloading the others. type is a MIME type (a standard label for a file's format), such as video/mp4 or video/webm, optionally with codecs: video/webm; codecs="vp9, opus". Today, MP4 with H.264 video and AAC audio plays everywhere; WebM and AV1 can be smaller. For audio, MP3 and AAC (in .m4a) play everywhere, and Opus is widely supported.

  5. 5

    Text placed inside <video> (after the sources) is fallback content for browsers that don't support the element at all. It is not shown when the browser supports video but can't play your file. That case fires an error event instead; handle it, or always include a format that plays everywhere.

  6. 6

    **preload** hints how much to download before the user presses play: none (nothing), metadata (only duration and size, the usual default) or auto (as much as the browser thinks wise). It's a hint, not an order: phones on mobile data often treat everything as none. autoplay overrides it, since the video must load to play.

Explain it without notes

01

What are the browser's autoplay rules, and how do you design around them?

02

How does the browser choose between several <source> elements, and why include type?

03

When is the content inside <video> displayed, and what should you do when a file fails to play?

04

What do preload values none, metadata and auto mean?

Practice

01

Write a video player for lesson.mp4 (1280×720) with controls, a poster lesson.jpg, no preloading, and a WebM version lesson.webm offered first.

02

Write a silent hero background video bg.mp4 that starts by itself, repeats and plays inline on iPhones.

03

Write JavaScript that starts an <audio id="song"> when a button #play is clicked and logs the error name if it fails.

Trade-offs

  • ↔

    Self-hosted <video> gives you full control and no third-party tracking, but you pay for bandwidth and must encode formats yourself; a video platform embed (Topic 5.5) is free to host but adds heavy scripts and its own branding.

  • ↔

    Autoplaying background video is eye-catching but costs data and battery and can distract; a poster image is often enough.

Done when you can

  • I can add a video or audio player with controls.

  • I know what muted, loop, autoplay, playsinline, poster and preload do.

  • I can explain the autoplay rules and handle the play() promise.

  • I can offer several formats with <source> and correct type values.

  • I know when fallback content shows and how to detect a failed source.