WasiLearn Code
HTML · Links, lists and images

Audio and video without a plugin

Playing a video used to mean embedding a Flash object and hoping the reader had the plugin. Now it is one element, and it works everywhere.

<video src="tour.mp4" controls width="640" height="360"></video>

<audio> works the same way with the same attributes. Both have a closing tag, unlike <img>, because there is something that goes between them — more on that shortly.

controls, or nobody can press play

<video src="tour.mp4"></video>       <!-- a still, silent rectangle -->
<video src="tour.mp4" controls></video>  <!-- play, pause, volume, timeline -->

Without controls, the browser renders the video with no interface at all. There is no play button, no scrubber, no volume. Unless you are writing JavaScript to drive playback yourself, that is a broken player.

controls is a boolean attribute: its presence is the whole meaning. controls="true" and controls="false" are both simply "present", so controls="false" shows the controls. To turn it off, remove the attribute. The same applies to muted, loop, autoplay and playsinline.

The controls the browser draws are its own, and they differ between Chrome, Safari and Firefox. They are also keyboard-accessible and screen-reader-labelled for free, which is a considerable amount of work you do not have to do.

Why your autoplay does not autoplay

<video src="tour.mp4" autoplay controls></video>

Nothing happens. No error, no warning in most cases, just a paused video.

This is deliberate, and it is one of the better decisions browsers have made. Audio starting by itself is hostile — someone opens a page in a background tab during a meeting, and your video announces itself to the room. So browsers block autoplay with sound, essentially universally.

Muted autoplay is allowed:

<video src="tour.mp4" autoplay muted loop playsinline></video>

That combination is the one behind every silent looping background video you have seen. muted satisfies the policy, loop restarts it, and playsinline stops iOS taking it fullscreen automatically.

muted must be on the element in the HTML. Setting it with JavaScript after the fact is often too late — the browser has already made its decision by then.

Two more things worth knowing. Some browsers permit unmuted autoplay if the reader has interacted with your site enough to have earned it, which means autoplay may work on your machine and fail for every first-time visitor — test in a private window. And a reader who has turned autoplay off in their browser settings gets no autoplay regardless of what you write. Never build a page that depends on it.

Several sources, one player

Not every browser plays every codec. Instead of a single src, offer a list:

<video controls width="640" height="360" poster="tour-still.jpg">
  <source src="tour.webm" type="video/webm">
  <source src="tour.mp4" type="video/mp4">
  Your browser cannot play this video.
  <a href="tour.mp4">Download it instead</a>.
</video>

The browser reads the <source> elements top to bottom and plays the first one it supports, so put your preferred format first. type lets it decide without downloading anything, so always include it.

In practice, MP4 with H.264 plays essentially everywhere, so a single MP4 is a reasonable choice. WebM is smaller at the same quality, which is why it goes first when you offer both.

Note there is no src on the <video> element itself here. If you write one, it wins and the <source> elements are ignored entirely.

poster is the still image shown before playback starts. Without it, browsers show either a blank rectangle or the first frame, which is often black.

The text inside the element

That sentence and link between the tags is fallback content. It is shown only by a browser that does not understand <video> at all — which today means almost nothing, but it costs one line and gives a genuine escape route rather than an empty box.

It is not a caption, it is not read by screen readers in supporting browsers, and it is not where accessibility lives. That is <track>.

Captions, with track

<video controls width="640" height="360">
  <source src="tour.mp4" type="video/mp4">
  <track src="tour-en.vtt" kind="captions" srclang="en" label="English" default>
  <track src="tour-ur.vtt" kind="captions" srclang="ur" label="اردو">
</video>

<track> is a void element pointing at a WebVTT file — a plain text file listing time ranges and the words spoken in them:

WEBVTT

00:00:00.000 --> 00:00:04.500
The stall opens at seven every morning.

00:00:04.500 --> 00:00:09.000
By eight there is usually a queue down the street.

Save it as .vtt, and note the first line must be exactly WEBVTT.

kind="captions" includes relevant sound as well as speech — a door slamming, music starting — and is for readers who cannot hear the audio. kind="subtitles" is a translation of the dialogue for readers who can hear it but do not speak the language. default marks the track shown initially.

Captions are the accessibility requirement for video in the same way alt is for images. They are also the thing that makes a video usable to the very large number of people watching with the sound off on a phone in public — which, depending on whose numbers you believe, is most of them.

One gotcha for local testing: VTT files are subject to cross-origin rules, so captions frequently fail when you open a page by double-clicking it and work fine the moment you serve it over http://localhost. If your captions do not appear, check that before you check the file.

Audio is the same element, smaller

<audio controls>
  <source src="interview.ogg" type="audio/ogg">
  <source src="interview.mp3" type="audio/mpeg">
  <a href="interview.mp3">Download the interview</a>
</audio>

Everything above applies: controls is required for a usable player, autoplay is blocked, multiple sources cover more browsers. There is no poster, and width/height have no meaning — the player is a fixed-height strip you can restyle with CSS.

For anything spoken, publish a transcript on the page as well. It is accessible, it is searchable, and unlike the audio itself it can be read in thirty seconds.

Hosting, honestly

Video is heavy. A few minutes at reasonable quality is tens of megabytes, and serving that from ordinary shared hosting to more than a handful of people at once will be slow for them and expensive for you. Self-hosting also means you handle the encoding, the multiple resolutions and the bandwidth yourself.

For a short clip, self-hosting with <video> is straightforward and gives you complete control. For anything longer or more popular, an embedded player from a video host does adaptive streaming — dropping the quality automatically on a weak connection — which is a lot of engineering to reproduce.

The shape to remember

<video controls width="640" height="360" poster="still.jpg" preload="metadata">
  <source src="clip.webm" type="video/webm">
  <source src="clip.mp4" type="video/mp4">
  <track src="clip-en.vtt" kind="captions" srclang="en" label="English" default>
  <p>Your browser cannot play this video.
     <a href="clip.mp4">Download it</a>.</p>
</video>

preload="metadata" is a good default: it fetches enough to know the duration and draw the timeline, without pulling the whole file down for someone who never presses play.

That closes the links, lists and images section. If a media file is not loading at all, the cause is nearly always the src — paths covers it — and images has the poster and layout-shift thinking that applies here too.

Prefer learning with a teacher?

WasiLearn Academy runs small-group classes covering this material, online and in Karachi.

See the classes