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.