|$ curl https://forge-ai.dev/api/markdown?path=docs/html/media
$cat docs/html-media.md
updated This week·45 min read·published

HTML Media

HTMLMediaIntermediateIntermediate
Introduction

Multimedia on the web encompasses images, video, audio, and interactive content. HTML provides native elements for embedding each type, with built-in controls, accessibility features, and performance optimizations. Understanding codecs and containers is essential for selecting the right format.

Codecs & Containers

A container format (like MP4 or WebM) holds video, audio, and metadata streams. The codec determines how each stream is compressed. Browser support varies — the safest approach is to provide multiple sources so the browser can choose the first supported format.

ContainerVideo CodecAudio CodecBrowser Support
MP4H.264, H.265 (HEVC)AAC, MP3Universal
WebMVP8, VP9, AV1Opus, VorbisChrome, Firefox, Edge
OGGTheoraVorbis, OpusFirefox, Chrome
AVIFAV1 (intra-frame)Chrome, Firefox, Edge
GIFUniversal (limited colors)

best practice

Provide resources in at least two formats: H.264 in MP4 for broad compatibility and VP9/AV1 in WebM for modern browsers. Use <picture> with multiple <source> elements to serve AVIF/WebP images with a JPEG fallback.
Images

The <img> element embeds images into the page. Modern attributes like srcset, sizes, loading, and decoding give developers fine-grained control over responsive behavior and performance.

AttributeValuesDescription
srcURLImage source URL (fallback)
altstringAlternative text (required for accessibility)
srcsetURL w/h descriptorList of candidate images (e.g., photo-800.jpg 800w)
sizesmedia query + sizeDescribes display size for srcset selection
loadinglazy | eagerDefers off-screen images (lazy = recommended)
decodingasync | sync | autoHint for browser image decode timing
fetchpriorityhigh | low | autoHint for relative fetch priority
width / heightpixelsIntrinsic dimensions (prevents CLS)
images.html
HTML
1<!-- Basic image with all performance attributes -->
2<img
3 src="photo.jpg"
4 alt="A sunset over the ocean with silhouetted palm trees"
5 width="1920"
6 height="1080"
7 loading="lazy"
8 decoding="async"
9 fetchpriority="low"
10/>
11
12<!-- Responsive images with srcset and sizes -->
13<img
14 src="photo-640.jpg"
15 srcset="
16 photo-640.jpg 640w,
17 photo-800.jpg 800w,
18 photo-1024.jpg 1024w,
19 photo-1280.jpg 1280w,
20 photo-1920.jpg 1920w
21 "
22 sizes="
23 (max-width: 640px) 100vw,
24 (max-width: 1024px) 80vw,
25 1200px
26 "
27 alt="Mountain landscape at dawn"
28 width="1920"
29 height="1080"
30 loading="lazy"
31 decoding="async"
32/>
33
34<!-- Hero image — eager loading, high priority -->
35<img
36 src="hero.jpg"
37 alt="Welcome to our platform"
38 width="1440"
39 height="600"
40 loading="eager"
41 decoding="async"
42 fetchpriority="high"
43/>
Picture Element & Figure

The <picture> element provides art direction and format selection. Multiple <source> elements declare media queries and format types. The <img> inside is always required as the fallback. <figure> with <figcaption> associates a caption with the image.

picture-figure.html
HTML
1<!-- Format selection with picture element -->
2<picture>
3 <!-- AVIF for modern browsers -->
4 <source
5 srcset="photo.avif"
6 type="image/avif"
7 />
8 <!-- WebP for Chrome/Firefox/Edge -->
9 <source
10 srcset="photo.webp"
11 type="image/webp"
12 />
13 <!-- JPEG fallback -->
14 <img
15 src="photo.jpg"
16 alt="City skyline at night"
17 width="1200"
18 height="800"
19 loading="lazy"
20 decoding="async"
21 />
22</picture>
23
24<!-- Art direction: different image for mobile -->
25<picture>
26 <source
27 srcset="photo-mobile.jpg"
28 media="(max-width: 640px)"
29 />
30 <source
31 srcset="photo-tablet.jpg"
32 media="(max-width: 1024px)"
33 />
34 <img
35 src="photo-desktop.jpg"
36 alt="Product showcase"
37 width="1920"
38 height="1080"
39 loading="eager"
40 />
41</picture>
42
43<!-- Figure with caption -->
44<figure>
45 <img
46 src="architecture.svg"
47 alt="System architecture diagram showing client-server flow"
48 width="800"
49 height="500"
50 loading="lazy"
51 />
52 <figcaption>
53 Figure 1: System architecture — clients connect through a load balancer
54 to application servers backed by a distributed database.
55 </figcaption>
56</figure>
🔥

pro tip

Inside <picture>, the <img> element is mandatory and serves as the fallback. All presentation attributes (width, height, loading, alt) go on the <img>, not on <source>. The browser picks the first matching <source> and downloads nothing else.
Video

The <video> element embeds video content with native browser controls. Multiple <source> elements provide format fallbacks. The <track> element adds captions, subtitles, and chapters. Text inside the element serves as a fallback for unsupported browsers.

AttributeValuesDescription
srcURLVideo source URL
controlsbooleanShow native playback controls
autoplaybooleanStart playback on load (requires muted)
loopbooleanRestart video when finished
mutedbooleanStart with audio muted
posterURLThumbnail shown before playback
preloadnone | metadata | autoHint for preloading behavior
playsinlineboolean (iOS)Play inline without fullscreen on iOS
width / heightpixelsDisplay dimensions (prevents CLS)
video.html
HTML
1<!-- Basic video with controls -->
2<video
3 controls
4 width="640"
5 height="360"
6 poster="thumbnail.jpg"
7 preload="metadata"
8>
9 <source src="video.mp4" type="video/mp4" />
10 <source src="video.webm" type="video/webm" />
11 <source src="video.ogg" type="video/ogg" />
12 <p>Your browser does not support the video element.</p>
13</video>
14
15<!-- Autoplaying muted background video -->
16<video
17 autoplay
18 loop
19 muted
20 playsinline
21 poster="bg-fallback.jpg"
22 width="1920"
23 height="1080"
24 preload="auto"
25>
26 <source src="bg-video.mp4" type="video/mp4" />
27 <source src="bg-video.webm" type="video/webm" />
28</video>
29
30<!-- Video with captions track -->
31<video
32 controls
33 width="640"
34 height="360"
35 poster="talk-poster.jpg"
36>
37 <source src="talk.mp4" type="video/mp4" />
38 <source src="talk.webm" type="video/webm" />
39
40 <track
41 kind="captions"
42 src="captions-en.vtt"
43 srclang="en"
44 label="English"
45 default
46 />
47 <track
48 kind="subtitles"
49 src="subtitles-es.vtt"
50 srclang="es"
51 label="Spanish"
52 />
53 <track
54 kind="chapters"
55 src="chapters.vtt"
56 srclang="en"
57 label="Chapters"
58 />
59
60 <p>Your browser does not support video captions.</p>
61</video>

warning

Most browsers block autoplay with audio. Always include muted when using autoplay. On iOS, playsinline is required for inline playback. The preload="none" attribute defers video loading but is ignored when autoplay is present.

Live preview of a styled video player placeholder:

preview
Audio

The <audio> element works like <video> but for sound-only content. It supports the same attributes — controls, autoplay, loop, preload — without the video-specific ones like poster and width/height.

AttributeValuesDescription
srcURLAudio source URL
controlsbooleanShow native playback controls
autoplaybooleanStart playback on load (browser restrictions apply)
loopbooleanRestart audio when finished
mutedbooleanStart with audio muted (useful with autoplay)
preloadnone | metadata | autoHint for preloading behavior
audio.html
HTML
1<!-- Basic audio player -->
2<audio
3 controls
4 preload="metadata"
5>
6 <source src="podcast.mp3" type="audio/mpeg" />
7 <source src="podcast.ogg" type="audio/ogg" />
8 <source src="podcast.wav" type="audio/wav" />
9 <p>Your browser does not support the audio element.</p>
10</audio>
11
12<!-- Autoplaying background music (muted) -->
13<audio
14 autoplay
15 loop
16 muted
17 preload="auto"
18>
19 <source src="background.mp3" type="audio/mpeg" />
20 <source src="background.ogg" type="audio/ogg" />
21</audio>
22
23<!-- Notification sound (played via JavaScript) -->
24<audio
25 id="notification"
26 preload="auto"
27 src="notification.mp3"
28></audio>
29
30<script>
31 const notif = document.getElementById('notification');
32 function playNotification() {
33 notif.currentTime = 0;
34 notif.play().catch(() => {
35 console.log('Audio blocked — user interaction required');
36 });
37 }
38</script>

info

Always include currentTime = 0 before calling play() for notification sounds — this ensures the audio restarts if already playing. Wrap play() in a catch to handle browser autoplay policy rejections gracefully.

Live preview of an audio player placeholder:

preview
Captions & Subtitles

The <track> element adds timed text tracks to video and audio elements. It supports captions (dialog + sound effects), subtitles (translated dialog), descriptions (audio description text), chapters (navigation markers), and metadata (custom data). Tracks use the WebVTT format (.vtt).

AttributeValuesDescription
kindcaptions | subtitles | descriptions | chapters | metadataType of timed text
srcURL to .vtt fileWebVTT file URL
srclangBCP 47 language tagLanguage of the track text
labelstringUser-visible label for track selection
defaultbooleanDefault enabled track
captions.html
HTML
1<!-- WebVTT file format: captions-en.vtt -->
2WEBVTT
3
400:00:01.000 --> 00:00:04.000
5Welcome to our tutorial on
6HTML media elements.
7
800:00:05.000 --> 00:00:09.500
9Today we will cover images,
10video, audio, and captions.
11
1200:00:10.000 --> 00:00:14.000
13<v Narrator>First, let's talk about
14the <picture&gt; element.</v>
15
1600:00:15.000 --> 00:00:20.000
17[camera shutter sound]
18
1900:00:22.000 --> 00:00:28.500
20With <c.highlight>srcset</c.highlight> you can
21serve different image resolutions.
22
23<!-- Styling in WebVTT -->
24<style>
25::cue(.highlight) {
26 background: rgba(0, 255, 65, 0.3);
27 color: #00FF41;
28}
29</style>
30
31<!-- HTML with multiple tracks -->
32<video
33 controls
34 width="640"
35 height="360"
36 poster="lecture-poster.jpg"
37>
38 <source src="lecture.mp4" type="video/mp4" />
39 <source src="lecture.webm" type="video/webm" />
40
41 <!-- English captions (dialog + sounds) -->
42 <track
43 kind="captions"
44 src="captions-en.vtt"
45 srclang="en"
46 label="English"
47 default
48 />
49
50 <!-- Spanish subtitles -->
51 <track
52 kind="subtitles"
53 src="subtitles-es.vtt"
54 srclang="es"
55 label="Español"
56 />
57
58 <!-- French subtitles -->
59 <track
60 kind="subtitles"
61 src="subtitles-fr.vtt"
62 srclang="fr"
63 label="Français"
64 />
65
66 <!-- Chapter markers for navigation -->
67 <track
68 kind="chapters"
69 src="chapters.vtt"
70 srclang="en"
71 label="Chapters"
72 />
73
74 <!-- Audio description track -->
75 <track
76 kind="descriptions"
77 src="descriptions-en.vtt"
78 srclang="en"
79 label="Audio Description"
80 />
81</video>

best practice

Captions include both dialog and relevant sound effects (e.g., "[camera shutter sound]"), while subtitles are translated dialog only. Always provide at least one kind="captions" track in the original language for accessibility. Mark it with default so it starts enabled. This is often a legal requirement (WCAG, ADA, Section 508).
Responsive Media

Responsive media ensures images and videos look great on any screen size. The srcset and sizes attributes handle resolution switching. The <picture> element with media queries handles art direction. CSS properties like object-fit and object-position control how media fills its container.

TechniqueCSS / HTMLUse Case
Resolution switchingsrcset + sizesSame image, different pixel densities
Art direction<picture> + mediaDifferent crop / composition per breakpoint
Format selection<picture> + typeAVIF/WebP with JPEG fallback
Fill containerobject-fit: coverImage/video fills container without distortion
Containobject-fit: containFull image visible, letterboxed
Aspect ratioaspect-ratio: 16/9Reserve space to prevent CLS
responsive-media.html
HTML
1<!-- CSS for responsive media -->
2<style>
3 .responsive-img {
4 max-width: 100%;
5 height: auto;
6 }
7
8 .hero-img {
9 width: 100%;
10 height: 60vh;
11 object-fit: cover;
12 object-position: center 20%;
13 }
14
15 .gallery {
16 display: grid;
17 grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
18 gap: 16px;
19 }
20
21 .gallery img {
22 width: 100%;
23 aspect-ratio: 4 / 3;
24 object-fit: cover;
25 border-radius: 8px;
26 }
27
28 .video-container {
29 width: 100%;
30 aspect-ratio: 16 / 9;
31 background: #000;
32 }
33
34 .video-container video {
35 width: 100%;
36 height: 100%;
37 object-fit: contain;
38 }
39
40 .background-video {
41 position: absolute;
42 width: 100%;
43 height: 100%;
44 object-fit: cover;
45 z-index: -1;
46 }
47</style>
48
49<!-- Responsive image with art direction -->
50<picture>
51 <!-- Mobile: portrait crop -->
52 <source
53 srcset="hero-mobile.jpg"
54 media="(max-width: 640px)"
55 />
56 <!-- Tablet: square crop -->
57 <source
58 srcset="hero-tablet.jpg"
59 media="(max-width: 1024px)"
60 />
61 <!-- Desktop: full landscape -->
62 <img
63 src="hero-desktop.jpg"
64 alt="Hero banner"
65 class="hero-img"
66 loading="eager"
67 decoding="async"
68 />
69</picture>
70
71<!-- Image gallery with consistent aspect ratio -->
72<div class="gallery">
73 <img
74 src="photo1.jpg"
75 alt="Gallery photo 1"
76 loading="lazy"
77 decoding="async"
78 />
79 <img
80 src="photo2.jpg"
81 alt="Gallery photo 2"
82 loading="lazy"
83 decoding="async"
84 />
85 <img
86 src="photo3.jpg"
87 alt="Gallery photo 3"
88 loading="lazy"
89 decoding="async"
90 />
91</div>
🔥

pro tip

Combine aspect-ratio with width and height attributes on images. The browser calculates the ratio from width/height to reserve space before the image loads, preventing Cumulative Layout Shift (CLS). The CSS object-fit property handles the actual fitting.
Performance

Media accounts for the majority of page weight. Optimizing image and video delivery is the single highest-impact performance improvement for most websites. Strategies include lazy loading, modern formats, responsive resolution selection, and CDN delivery.

TechniqueImpactImplementation
Lazy loadingDefers off-screen imagesloading="lazy"
Async decodeNon-blocking image decodedecoding="async"
Fetch priorityCritical images load firstfetchpriority="high"
Modern formats40-60% smaller filesAVIF, WebP
Responsive srcsetRight size for viewportsrcset + sizes
Image CDNOn-the-fly resize + optimizationCloudinary, Imgix, Next.js Image
Video preloadControl video bufferingpreload="metadata"
Width/heightPrevents CLSwidth + height attributes
media-performance.html
HTML
1<!-- Performance-optimized image -->
2<img
3 src="photo-640.jpg"
4 srcset="
5 photo-320.jpg 320w,
6 photo-640.jpg 640w,
7 photo-960.jpg 960w,
8 photo-1280.jpg 1280w,
9 photo-1920.jpg 1920w
10 "
11 sizes="
12 (max-width: 400px) 100vw,
13 (max-width: 800px) 80vw,
14 1200px
15 "
16 alt="Performance optimized image"
17 width="1920"
18 height="1080"
19 loading="lazy"
20 decoding="async"
21 fetchpriority="low"
22/>
23
24<!-- Critical hero image (high priority) -->
25<img
26 src="hero-1920.jpg"
27 srcset="
28 hero-640.jpg 640w,
29 hero-1024.jpg 1024w,
30 hero-1920.jpg 1920w
31 "
32 sizes="100vw"
33 alt="Hero banner"
34 width="1920"
35 height="800"
36 loading="eager"
37 decoding="sync"
38 fetchpriority="high"
39/>
40
41<!-- Preload critical image in <head> -->
42<link
43 rel="preload"
44 href="hero-1920.jpg"
45 as="image"
46 imagesrcset="hero-640.jpg 640w, hero-1024.jpg 1024w, hero-1920.jpg 1920w"
47 imagesizes="100vw"
48/>
49
50<!-- Video with metadata preload -->
51<video
52 controls
53 width="640"
54 height="360"
55 poster="thumbnail.jpg"
56 preload="metadata"
57>
58 <source src="video.mp4" type="video/mp4" />
59</video>

best practice

Use an image CDN that supports on-the-fly transformation (resize, crop, format conversion). This eliminates the need to manually generate multiple image variants. The CDN handles WebP/AVIF negotiation, quality optimization, and cache headers automatically. For video, host on a CDN with byte-range request support for efficient seeking.

Live preview of an optimized responsive image placeholder with CDN-style URL parameters:

preview
Accessibility

Media accessibility ensures users with visual, auditory, or cognitive disabilities can perceive and understand media content. This includes alt text for images, captions and transcripts for audio/video, and proper ARIA labeling for interactive media controls.

RequirementElementImplementation
Alt text<img>Descriptive alt attribute (decorative: alt="")
Captions<video> + <track>kind="captions" with WebVTT file
TranscriptsHTML textFull text transcript alongside or linked
Audio description<track>kind="descriptions" narrating visual content
aria-labelButtons, playersLabel for custom play/pause controls
Figure caption<figcaption>Associates descriptive caption with media
Reduced motionCSS / prefers-reduced-motionRespect user preference to reduce animations
media-accessibility.html
HTML
1<!-- Accessible image with descriptive alt text -->
2<figure>
3 <img
4 src="chart.svg"
5 alt="Bar chart showing revenue growth from $2.1M in Q1 to $3.8M in Q4 2026, with steady upward trend across all four quarters"
6 width="800"
7 height="500"
8 />
9 <figcaption>
10 Figure 2: Annual revenue growth by quarter — 81% increase from Q1 to Q4.
11 </figcaption>
12</figure>
13
14<!-- Decorative image (empty alt) -->
15<img
16 src="divider.svg"
17 alt=""
18 role="presentation"
19 width="100"
20 height="20"
21/>
22
23<!-- Accessible video with captions and transcript -->
24<video
25 controls
26 width="640"
27 height="360"
28 aria-label="Tutorial: How to deploy a web application"
29>
30 <source src="deploy-tutorial.mp4" type="video/mp4" />
31 <track
32 kind="captions"
33 src="deploy-captions.vtt"
34 srclang="en"
35 label="English"
36 default
37 />
38 <p>
39 Your browser doesn't support video.
40 <a href="deploy-transcript.html">Read the transcript</a>.
41 </p>
42</video>
43
44<!-- Transcript link -->
45<section aria-label="Video transcript">
46 <h3>Transcript</h3>
47 <p>
48 In this tutorial, we walk through deploying a web application
49 to production. First, we configure the build process...
50 </p>
51</section>
52
53<!-- Custom accessible controls -->
54<button
55 type="button"
56 aria-label="Play video"
57 aria-pressed="false"
58 onclick="togglePlay()"
59>
60 <span aria-hidden="true">▶</span>
61</button>
62
63<!-- Respect reduced motion -->
64<style>
65 @media (prefers-reduced-motion: reduce) {
66 video.autoplay {
67 autoplay: none;
68 }
69 * {
70 animation-duration: 0.01ms !important;
71 transition-duration: 0.01ms !important;
72 }
73 }
74</style>

best practice

Alt text should describe the content and function of an image, not its appearance. For complex images like charts, include the data in text form nearby. Set alt="" with role="presentation" for purely decorative images so screen readers ignore them. Always provide a text transcript for any video or audio content.
Best Practices

Format Selection

Content TypeRecommended FormatFallback
PhotosAVIF (best quality/size) or WebPJPEG
Art / GraphicsWebP or PNGPNG
Logos / IconsSVGPNG (with @2x for retina)
AnimatedWebP or AVIF (animated) or MP4 videoGIF (last resort)
VideoH.264 MP4 + VP9/AV1 WebM
AudioMP3 (broad) or AACOGG Vorbis

Responsive Images Checklist

Always include width and height attributes to prevent Cumulative Layout Shift (CLS)
Use loading=lazy for images below the fold; use loading=eager with fetchpriority=high for the hero/LCP image
Provide srcset with at least 3 breakpoints (640w, 1024w, 1920w) for responsive resolution switching
Use the picture element with type attributes for AVIF and WebP format selection, with a JPEG fallback
Set decoding=async on all images to avoid blocking the main thread during decode
Use an image CDN (Cloudinary, Imgix, Next.js Image) for automatic resizing, format conversion, and cache optimization
Compress images to the smallest acceptable quality — aim for 60-80% quality for photographs
For hero/background images, use object-fit: cover and object-position to control crop behavior across viewports
Use the preload link tag (rel=preload as=image) for the LCP image to prioritize its fetch
Serve images from a CDN with proper cache headers (Cache-Control: public, max-age=31536000, immutable)
🔥

pro tip

Use the fetchpriority attribute (not importance) to hint the browser about image loading priority. Set fetchpriority="high" on the Largest Contentful Paint (LCP) image and fetchpriority="low" on non-critical images. This works alongside loading="lazy" — a lazy image with high priority will load sooner than one with low priority.

Live preview showing a responsive gallery with proper aspect ratios and object-fit:

preview
$Blueprint — Engineering Documentation·Section ID: HTML-05·Revision: 1.0