Design.dev design.dev

HTML Media Elements Guide

Complete reference for HTML media elements including video, audio, picture, and modern responsive image techniques. Learn attributes, accessibility best practices, and performance optimization for rich media content.

Introduction

HTML5 introduced native media elements that enable embedding audio, video, and responsive images without plugins. These elements provide accessibility features, responsive capabilities, and fine-grained control over media presentation.

Core Media Elements

<video>

Embed video content with controls and fallbacks

Native video playback

<audio>

Embed audio content like music or podcasts

Native audio playback

<picture>

Responsive images with art direction

Format & size selection

<source>

Multiple media sources for compatibility

Fallback support

Browser Support: All modern media elements are fully supported in current browsers. Always provide fallback content for older browsers and multiple formats for maximum compatibility.

Video Element

The <video> element embeds video content in your web page with native browser controls and JavaScript API access.

Basic Video

<video src="video.mp4" controls>
  Your browser doesn't support video.
</video>

Video with Multiple Sources

<video controls width="640" height="360" poster="thumbnail.jpg">
  <source src="video.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">
  <p>Your browser doesn't support HTML video. 
     <a href="video.mp4">Download the video</a>.</p>
</video>

Browsers will choose the first supported format. WebM typically offers better compression than MP4.

Video Attributes

Attribute Description Example
src URL of the video file src="video.mp4"
controls Display playback controls (play, pause, volume) controls
autoplay Start playing automatically (requires muted) autoplay muted
loop Repeat video when finished loop
muted Start with audio muted muted
poster Image to show before playback starts poster="thumb.jpg"
width Video display width in pixels width="640"
height Video display height in pixels height="360"
preload Hint for how much to buffer (none, metadata, auto) preload="metadata"
playsinline Play inline on mobile (not fullscreen) playsinline
crossorigin CORS settings (anonymous, use-credentials) crossorigin="anonymous"

Background Video Pattern

<video autoplay loop muted playsinline class="bg-video">
  <source src="background.webm" type="video/webm">
  <source src="background.mp4" type="video/mp4">
</video>
.bg-video {
  position: fixed;
  top: 50%;
  left: 50%;
  min-width: 100%;
  min-height: 100%;
  width: auto;
  height: auto;
  transform: translate(-50%, -50%);
  z-index: -1;
  object-fit: cover;
}

Important: Autoplay only works when the video is muted. Browsers block unmuted autoplay to protect user experience.

Recommended Video Formats

WebM (VP9/AV1)

Best compression, open format

Chrome, Firefox, Edge

MP4 (H.264)

Universal compatibility

All browsers

MP4 (H.265/HEVC)

Better compression, limited support

Safari, some mobile

Audio Element

The <audio> element embeds sound content like music, podcasts, or sound effects.

Basic Audio

<audio src="audio.mp3" controls>
  Your browser doesn't support audio.
</audio>

Audio with Multiple Sources

<audio controls>
  <source src="audio.opus" type="audio/opus">
  <source src="audio.ogg" type="audio/ogg">
  <source src="audio.mp3" type="audio/mpeg">
  <p>Your browser doesn't support HTML audio. 
     <a href="audio.mp3">Download the audio</a>.</p>
</audio>

Audio Attributes

Attribute Description Example
src URL of the audio file src="audio.mp3"
controls Display audio controls controls
autoplay Start playing automatically (avoid for UX) autoplay
loop Repeat audio when finished loop
muted Start with audio muted muted
preload Buffer hint (none, metadata, auto) preload="none"
crossorigin CORS settings crossorigin="anonymous"

Background Music Pattern

<audio id="bgMusic" loop>
  <source src="background.opus" type="audio/opus">
  <source src="background.mp3" type="audio/mpeg">
</audio>

<button id="musicToggle">Play Music</button>
const audio = document.getElementById('bgMusic');
const toggle = document.getElementById('musicToggle');

toggle.addEventListener('click', () => {
  if (audio.paused) {
    audio.play();
    toggle.textContent = 'Pause Music';
  } else {
    audio.pause();
    toggle.textContent = 'Play Music';
  }
});

UX Tip: Avoid autoplay for audio. Let users control when sound plays. If you must autoplay, provide an obvious mute/pause button.

Recommended Audio Formats

Opus

Best quality at low bitrates

Chrome, Firefox, Edge

MP3

Universal compatibility

All browsers

AAC (M4A)

Good quality, Apple preferred

Safari, most browsers

Picture Element

The <picture> element provides art direction and format selection for responsive images. The browser selects the most appropriate source based on device capabilities and viewport size.

Basic Picture with Format Selection

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Description" loading="lazy">
</picture>

Browsers will use AVIF if supported, fall back to WebP, then JPEG. The <img> tag is required and serves as the fallback.

Art Direction (Different Images for Different Screens)

<picture>
  <source media="(min-width: 1200px)" srcset="hero-desktop.jpg">
  <source media="(min-width: 768px)" srcset="hero-tablet.jpg">
  <img src="hero-mobile.jpg" alt="Hero image" loading="lazy">
</picture>

Show different crops or compositions based on viewport width. Great for hero images where mobile needs a different composition than desktop.

Combined Format + Art Direction

<picture>
  <!-- Desktop: 1200px+ -->
  <source media="(min-width: 1200px)" 
          srcset="hero-desktop.avif" type="image/avif">
  <source media="(min-width: 1200px)" 
          srcset="hero-desktop.webp" type="image/webp">
  <source media="(min-width: 1200px)" 
          srcset="hero-desktop.jpg">
  
  <!-- Tablet: 768px - 1199px -->
  <source media="(min-width: 768px)" 
          srcset="hero-tablet.avif" type="image/avif">
  <source media="(min-width: 768px)" 
          srcset="hero-tablet.webp" type="image/webp">
  <source media="(min-width: 768px)" 
          srcset="hero-tablet.jpg">
  
  <!-- Mobile: default -->
  <source srcset="hero-mobile.avif" type="image/avif">
  <source srcset="hero-mobile.webp" type="image/webp">
  <img src="hero-mobile.jpg" alt="Hero banner" loading="lazy">
</picture>

Dark Mode Images

<picture>
  <source srcset="logo-dark.png" 
          media="(prefers-color-scheme: dark)">
  <img src="logo-light.png" alt="Company logo">
</picture>

Best Practice: Always order sources from most specific to least specific. The browser picks the first matching source. The <img> element is required and provides alt text and dimensions.

Source Element

The <source> element specifies multiple media resources for <picture>, <video>, and <audio> elements.

Source Attributes

Attribute Used With Description
src video, audio URL of media file
srcset picture Image URLs and descriptors (1x, 2x, width)
type all MIME type of the resource
media picture Media query for when to use this source
sizes picture Image sizes for different viewport widths
width picture Intrinsic width (experimental)
height picture Intrinsic height (experimental)

MIME Types Reference

Format MIME Type Use Case
Images
AVIF image/avif Best compression, modern browsers
WebP image/webp Great compression, wide support
JPEG image/jpeg Universal fallback
PNG image/png Transparency, lossless
Video
WebM (VP9) video/webm Open format, good compression
WebM (AV1) video/webm; codecs="av01" Best compression, modern browsers
MP4 (H.264) video/mp4 Universal compatibility
Audio
Opus audio/opus Best quality/size ratio
Ogg Vorbis audio/ogg Open format
MP3 audio/mpeg Universal compatibility
AAC audio/aac Good quality, Apple devices

Responsive Images

Responsive images adapt to different screen sizes and resolutions using srcset and sizes attributes.

Resolution Switching (1x, 2x, 3x)

<img src="image.jpg"
     srcset="image.jpg 1x,
             [email protected] 2x,
             [email protected] 3x"
     alt="Description"
     loading="lazy">

Provide higher resolution images for high-DPI displays (Retina, 4K). The browser chooses based on device pixel ratio.

Width Descriptors (Responsive Sizing)

<img src="image-800.jpg"
     srcset="image-400.jpg 400w,
             image-800.jpg 800w,
             image-1200.jpg 1200w,
             image-1600.jpg 1600w"
     sizes="(max-width: 600px) 100vw,
            (max-width: 1200px) 50vw,
            800px"
     alt="Description"
     loading="lazy">

The w descriptor specifies image width. sizes tells the browser how wide the image will be displayed at different viewport sizes.

Understanding Sizes Attribute

<!-- Image is full width on mobile, half width on tablet, 800px max on desktop -->
<img srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 100vw,
            (max-width: 1200px) 50vw,
            800px"
     src="medium.jpg"
     alt="Responsive image">

Common Sizes Patterns

Use Case Sizes Attribute
Full width sizes="100vw"
Half width sizes="50vw"
Fixed max width sizes="800px"
Responsive grid item sizes="(min-width: 1200px) 33vw, (min-width: 768px) 50vw, 100vw"
Content width container sizes="(min-width: 1200px) 1140px, (min-width: 992px) 960px, 100vw"
Sidebar image sizes="(min-width: 768px) 300px, 100vw"

Performance Tip: Using srcset and sizes correctly can reduce image payload by 50-70% on mobile devices while maintaining quality on desktop and high-DPI displays.

Modern Image Attributes

Modern HTML provides attributes to optimize image loading and rendering performance.

Lazy Loading

<img src="image.jpg" alt="Description" loading="lazy">

Defers loading off-screen images until the user scrolls near them. Native browser feature with excellent performance and no JavaScript required.

Loading Attribute Values

Value Behavior Use Case
lazy Load when near viewport Below-the-fold images, galleries
eager Load immediately Above-the-fold images, critical content
(default) Browser decides Most images

Decoding Attribute

<img src="large-image.jpg" 
     alt="Description" 
     loading="lazy"
     decoding="async">

Decoding Values

Value Behavior Use Case
async Decode off main thread Large images, doesn't block rendering
sync Decode before displaying Critical images that must appear immediately
auto Browser decides (default) Most images

Fetchpriority Attribute

<!-- Hero image: load with high priority -->
<img src="hero.jpg" 
     alt="Hero image" 
     fetchpriority="high">

<!-- Decorative images: lower priority -->
<img src="decoration.jpg" 
     alt="" 
     loading="lazy" 
     fetchpriority="low">

Width and Height Attributes

<img src="image.jpg" 
     alt="Description"
     width="800" 
     height="600"
     loading="lazy">

Always specify width and height to prevent layout shift (CLS). Browsers reserve space before the image loads. Use CSS to make responsive:

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

Core Web Vitals: Using width and height attributes with loading="lazy" improves Cumulative Layout Shift (CLS) scores significantly.

Complete Optimized Image Example

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" 
       alt="Descriptive alt text"
       width="800"
       height="600"
       loading="lazy"
       decoding="async"
       fetchpriority="low">
</picture>

Captions & Subtitles

The <track> element provides text tracks for <video> and <audio> elements, including captions, subtitles, and descriptions.

Adding Captions to Video

<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="captions" 
         src="captions-en.vtt" 
         srclang="en" 
         label="English" 
         default>
  <track kind="captions" 
         src="captions-es.vtt" 
         srclang="es" 
         label="Español">
</video>

Track Attributes

Attribute Description Example
kind Type of text track captions, subtitles, descriptions
src URL of the track file (.vtt) src="captions.vtt"
srclang Language of the track srclang="en"
label User-visible title label="English"
default Enable this track by default default

Track Kind Values

Kind Purpose Use Case
captions Transcription and sound descriptions For deaf/hard of hearing users
subtitles Translation of dialogue For users who don't understand the language
descriptions Text description of visuals For blind users
chapters Chapter titles for navigation For navigating long videos
metadata Metadata for scripts For JavaScript interaction

WebVTT Format Example

WEBVTT

00:00:00.000 --> 00:00:04.000
Welcome to our video tutorial.

00:00:04.500 --> 00:00:08.000
Today we'll learn about HTML media elements.

00:00:08.500 --> 00:00:12.000
Let's start with the video element.

Accessibility

Making media accessible ensures all users can access your content regardless of disability.

Images Accessibility

<!-- Informative image -->
<img src="chart.jpg" alt="Sales increased 50% in Q4">

<!-- Decorative image -->
<img src="decoration.jpg" alt="" role="presentation">

<!-- Complex image -->
<figure>
  <img src="diagram.jpg" alt="System architecture diagram">
  <figcaption>
    The diagram shows three layers: presentation, 
    business logic, and data storage.
  </figcaption>
</figure>

Alt Text Guidelines

Informative Images

Describe the content and function

alt="Search icon"

Decorative Images

Use empty alt text

alt=""

Functional Images

Describe the action

alt="Submit form"

Complex Images

Use figcaption or longdesc

<figcaption>

Video Accessibility

<video controls 
       aria-label="Product demonstration video">
  <source src="demo.mp4" type="video/mp4">
  
  <!-- Captions for deaf/hard of hearing -->
  <track kind="captions" 
         src="captions-en.vtt" 
         srclang="en" 
         label="English" 
         default>
  
  <!-- Descriptions for blind users -->
  <track kind="descriptions" 
         src="descriptions-en.vtt" 
         srclang="en" 
         label="Audio Descriptions">
  
  <!-- Text fallback -->
  <p>Watch our <a href="transcript.html">video transcript</a></p>
</video>

Audio Accessibility

<audio controls aria-label="Podcast episode 42">
  <source src="podcast.mp3" type="audio/mpeg">
  
  <!-- Transcript for deaf users -->
  <p>Read the <a href="transcript.html">full transcript</a></p>
</audio>

<!-- Or include transcript inline -->
<details>
  <summary>Transcript</summary>
  <p>Welcome to episode 42...</p>
</details>

Accessibility Checklist

Element Requirement WCAG Level
Images Meaningful alt text or alt="" A
Video Captions for audio content A
Video Audio descriptions for visual content AA
Audio Transcript available A
Video Keyboard controls accessible A
All Media No autoplay with sound A
All Media ARIA labels for context A

Legal Requirement: Many jurisdictions require video captions and transcripts. WCAG 2.1 Level AA compliance is often legally mandated for government and public-facing websites.

Performance Best Practices

Optimize media delivery to improve page load times and user experience.

Image Optimization

Use Modern Formats

AVIF (best) → WebP → JPEG/PNG

50-70% smaller files

Lazy Load

Defer off-screen images

loading="lazy"

Set Dimensions

Prevent layout shift

width + height

Compress Images

Reduce file size without quality loss

Use image optimizer tools

Image Size Guidelines

Use Case Max Width Max File Size
Hero Image 2400px 200-300 KB
Content Image 1200px 100-150 KB
Thumbnail 400px 20-50 KB
Icon 100px 5-10 KB (use SVG)
Background 2000px 100-200 KB

Video Optimization

<!-- Optimize video delivery -->
<video controls 
       preload="metadata" 
       poster="thumbnail.jpg">
  <source src="video-480p.webm" type="video/webm"
          media="(max-width: 768px)">
  <source src="video-720p.webm" type="video/webm"
          media="(max-width: 1200px)">
  <source src="video-1080p.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">
</video>

Preload Values

Value Behavior Use Case
none Don't preload anything Below-the-fold videos, save bandwidth
metadata Load duration and dimensions Most videos (recommended default)
auto Load entire file Critical videos user will likely play

Video Compression Tips

Resolution

Mobile: 480p, Tablet: 720p, Desktop: 1080p

Match viewport size

Bitrate

480p: 1 Mbps, 720p: 2.5 Mbps, 1080p: 5 Mbps

Balance quality/size

Frame Rate

24-30 fps for most content

Lower = smaller files

Codec

WebM (VP9/AV1) for best compression

+ MP4 (H.264) fallback

CDN and Hosting

<!-- Serve from CDN with cache headers -->
<img src="https://cdn.example.com/images/photo.jpg"
     alt="Photo"
     loading="lazy"
     crossorigin="anonymous">

<!-- Video streaming service -->
<video controls>
  <source src="https://cdn.example.com/videos/adaptive.m3u8"
          type="application/x-mpegURL">
</video>

Performance Impact: Media files are typically 60-80% of page weight. Optimizing images and video can improve load times by 2-5 seconds and significantly improve Core Web Vitals scores.

Common Patterns

Reusable patterns for common media use cases.

Responsive Background Video

<div class="hero">
  <video autoplay loop muted playsinline class="hero__video">
    <source src="hero.webm" type="video/webm">
    <source src="hero.mp4" type="video/mp4">
  </video>
  <div class="hero__content">
    <h1>Welcome</h1>
  </div>
</div>
.hero {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

.hero__video {
  position: absolute;
  top: 50%;
  left: 50%;
  min-width: 100%;
  min-height: 100%;
  width: auto;
  height: auto;
  transform: translate(-50%, -50%);
  object-fit: cover;
  z-index: -1;
}

.hero__content {
  position: relative;
  z-index: 1;
  color: white;
  text-align: center;
  padding: 2rem;
}

Image Gallery with Lazy Loading

<div class="gallery">
  <img src="image1.jpg" alt="Gallery image 1" 
       width="400" height="300" loading="eager">
  <img src="image2.jpg" alt="Gallery image 2" 
       width="400" height="300" loading="lazy">
  <img src="image3.jpg" alt="Gallery image 3" 
       width="400" height="300" loading="lazy">
  <img src="image4.jpg" alt="Gallery image 4" 
       width="400" height="300" loading="lazy">
</div>

First image loads eagerly (visible), rest lazy load as user scrolls.

Responsive Product Image

<picture>
  <source media="(min-width: 1200px)"
          srcset="product-large.avif" 
          type="image/avif">
  <source media="(min-width: 1200px)"
          srcset="product-large.webp" 
          type="image/webp">
  <source media="(min-width: 768px)"
          srcset="product-medium.avif" 
          type="image/avif">
  <source media="(min-width: 768px)"
          srcset="product-medium.webp" 
          type="image/webp">
  <source srcset="product-small.avif" 
          type="image/avif">
  <source srcset="product-small.webp" 
          type="image/webp">
  <img src="product-medium.jpg" 
       alt="Product name - Product description"
       width="800"
       height="800"
       loading="lazy">
</picture>

Video Thumbnail with Playback

<div class="video-wrapper">
  <video id="myVideo" 
         poster="thumbnail.jpg" 
         preload="none">
    <source src="video.mp4" type="video/mp4">
  </video>
  <button class="play-btn" aria-label="Play video">
    ▶ Play
  </button>
</div>
const video = document.getElementById('myVideo');
const playBtn = document.querySelector('.play-btn');

playBtn.addEventListener('click', () => {
  if (video.paused) {
    video.play();
    playBtn.textContent = '❚❚ Pause';
  } else {
    video.pause();
    playBtn.textContent = '▶ Play';
  }
});

Audio Player with Playlist

<audio id="player" controls>
  <source src="" type="audio/mpeg">
</audio>

<ul class="playlist">
  <li><button data-src="track1.mp3">Track 1</button></li>
  <li><button data-src="track2.mp3">Track 2</button></li>
  <li><button data-src="track3.mp3">Track 3</button></li>
</ul>
const player = document.getElementById('player');
const buttons = document.querySelectorAll('.playlist button');

buttons.forEach(button => {
  button.addEventListener('click', () => {
    const src = button.dataset.src;
    player.src = src;
    player.play();
  });
});

Gotchas & Tips

Autoplay Restrictions

Browsers block unmuted autoplay. Always mute autoplay videos: autoplay muted

Mobile Video Playback

Add playsinline to prevent fullscreen on iOS. Without it, videos open in fullscreen player.

Picture Element

The <img> inside <picture> is required. It provides the fallback and alt text.

Lazy Loading

Don't lazy load above-the-fold images. Use loading="eager" or omit the attribute for critical images.

Width and Height

Always set dimensions to prevent layout shift, even with max-width: 100% in CSS.

Format Order

List formats from smallest to largest: AVIF → WebP → JPEG. Browser picks first supported format.

Video Poster

Always provide a poster image. It shows while loading and if video fails.

CORS Issues

Add crossorigin="anonymous" when manipulating media with canvas or loading from CDN.

Empty Alt Text

Decorative images must have alt="" (empty, not missing). Missing alt is an accessibility error.

Preload Performance

Use preload="metadata" for most videos. auto downloads entire video and hurts page performance.

Sizes Attribute

If using srcset with width descriptors (w), you must include the sizes attribute.

Track Element

WebVTT files must be served with correct MIME type: text/vtt and proper CORS headers.

Testing Tip: Test media on real devices, especially mobile. Throttle network to 3G to identify performance issues. Use Lighthouse to check Core Web Vitals impact.