The four multimedia building blocks
"Multimedia" just means more than text and links — sound, video, and rich images. HTML gives you four tools for it. You'll use each one in this tutorial.
<video>
Plays video files you host yourself — .mp4, .webm — with built-in controls.
<audio>
Plays sound — podcasts, music, voiceovers — from .mp3 or .ogg files.
<iframe> embeds
Drops in a YouTube or Vimeo player so you don't host huge video files yourself.
<picture>
Serves different image files by screen size or format — smaller files, sharper results.
<video>. Long videos, or anything already on YouTube → embed an
<iframe> so their servers handle the bandwidth, not yours.
The <video> tag
The <video> element plays a video file right on your page. Add the
controls attribute and the browser draws the play button, scrubber, volume, and
fullscreen for you. Below is a real video playing through the native player.
Press play · drag the scrubber · go fullscreen — all built in, no JavaScript
The markup
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Native video player</title> <link rel="stylesheet" href="styles.css"> </head> <body> <!-- The frame just centres + rounds the player --> <div class="video-frame"> <!-- controls = show the play/scrub/volume bar --> <video controls preload="metadata" playsinline poster="cover.jpg"> <!-- The browser plays the first source it understands --> <source src="clip.mp4" type="video/mp4"> <source src="clip.webm" type="video/webm"> <!-- Fallback text for very old browsers --> Your browser doesn't support the video tag. </video> </div> <p class="media-caption">Press play · drag the scrubber · go fullscreen — all built in, no JavaScript</p> </body> </html>
body { background: #0a0a0a; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } /* Centred, rounded frame with a soft drop shadow */ .video-frame { width: 100%; max-width: 640px; margin: 0 auto; border-radius: 12px; overflow: hidden; box-shadow: 0 16px 40px rgba(0,0,0,.5); background: #000; } .video-frame video { width: 100%; height: auto; display: block; } /* Caption under the player */ .media-caption { text-align: center; font-size: 0.82rem; color: #71717a; margin-top: 12px; }
The attributes you'll actually use
| Attribute | What it does |
|---|---|
controls | Shows the play bar, scrubber, volume, and fullscreen button. |
poster | An image shown before the video plays — like a thumbnail. |
autoplay | Starts playing on load. Browsers only allow this if muted is also set. |
muted | Starts with no sound. Required for autoplay to work. |
loop | Replays from the start when it ends — good for ambient background clips. |
preload | metadata loads just the length/dimensions; none waits until play; auto loads the whole file. |
playsinline | Plays inside the page on iPhone instead of forcing fullscreen. |
poster image makes the player look intentional before
anyone hits play.
The <audio> tag
Same idea as <video>, just for sound. Add controls and you get
a play button, scrubber, and volume slider. Perfect for podcast episodes, music previews, or
narrated walkthroughs. Two real audio tracks are wired up below.
The markup
<div class="audio-row"> <!-- Track 1 --> <div class="audio-card"> <div class="audio-art"><i class="ph-fill ph-music-note"></i></div> <div class="audio-body"> <div class="audio-meta"><strong>Light the Way</strong><span>Track 1</span></div> <audio controls preload="metadata"> <source src="Light%20the%20Way.mp3" type="audio/mpeg"> Your browser doesn't support the audio element. </audio> </div> </div> <!-- Track 2 --> <div class="audio-card"> <div class="audio-art"><i class="ph-fill ph-microphone"></i></div> <div class="audio-body"> <div class="audio-meta"><strong>What I Learned</strong><span>Track 2</span></div> <audio controls preload="metadata"> <source src="What%20I%20Learned.mp3" type="audio/mpeg"> Your browser doesn't support the audio element. </audio> </div> </div> </div>
/* Stack the two cards in a column */ .audio-row { display: flex; flex-direction: column; gap: 16px; max-width: 540px; margin: 0 auto; } .audio-card { display: flex; align-items: center; gap: 16px; background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.08); border-radius: 12px; padding: 16px 18px; } /* The pink-to-purple gradient square with the icon */ .audio-art { width: 52px; height: 52px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: #fff; background: linear-gradient(135deg, #f43f5e, #a855f7); } .audio-body { flex: 1; min-width: 0; } .audio-meta strong { display: block; font-size: 0.95rem; color: #fff; } .audio-meta span { font-size: 0.8rem; color: #a1a1aa; } .audio-card audio { width: 100%; margin-top: 10px; }
My Song.mp3 must be written as
My%20Song.mp3 in the src (that's what %20 means). The
safest habit is to rename media files with hyphens: my-song.mp3.
Embedding YouTube & Vimeo
For videos already online, don't download and re-host them — embed an <iframe>.
On YouTube, click Share → Embed and copy the code. The trick is making it
responsive: wrap it in a 16:9 box so it scales on phones instead of overflowing.
Resize the window — the frame keeps its 16:9 shape and never overflows
The code
<!-- The 16:9 ratio box. Drop a real <iframe> inside --> <!-- and it fills the frame; the placeholder below --> <!-- just shows the empty shape until you do. --> <div class="embed-16x9"> <!-- Placeholder shown while the box is empty --> <div class="embed-placeholder"> <i class="ph-duotone ph-youtube-logo"></i> <strong>Your embedded player goes here</strong> <small>This dashed box keeps a perfect 16:9 ratio at any width — paste your iframe inside it</small> </div> <!-- When ready, replace the placeholder with: --> <iframe src="https://www.youtube.com/embed/VIDEO_ID" title="YouTube video player" allowfullscreen></iframe> </div>
body { background: #0a0a0a; } /* The ratio box: keeps 16:9 at any width. */ /* Dashed border + striped fill show it's empty. */ .embed-16x9 { position: relative; width: 100%; max-width: 640px; margin: 0 auto; aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden; background: repeating-linear-gradient(45deg, #18181b, #18181b 14px, #141417 14px, #141417 28px); border: 1px dashed rgba(244,63,94,.4); display: flex; align-items: center; justify-content: center; text-align: center; } /* A real iframe drops in and fills the box completely */ .embed-16x9 iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; } /* The "drop your player here" placeholder */ .embed-placeholder { color: #a1a1aa; padding: 20px; } .embed-placeholder i { font-size: 2.4rem; color: #f43f5e; display: block; margin-bottom: 10px; } .embed-placeholder strong { color: #fff; display: block; margin-bottom: 4px; } .embed-placeholder small { font-size: 0.8rem; }
VIDEO_ID with the part after watch?v= in a
YouTube URL. For youtube.com/watch?v=dQw4w9WgXcQ the embed src becomes
youtube.com/embed/dQw4w9WgXcQ.
Rich images with <picture>
Images are multimedia too. The <picture> element lets you ship a small image
to phones and a large one to desktops, or a modern .webp with a .jpg
fallback — the browser picks the best one and never downloads the rest.
Swap by screen size
<picture> <!-- Phone: load the small image --> <source media="(max-width: 600px)" srcset="hero-small.jpg"> <!-- Tablet and up: the big image --> <source media="(min-width: 601px)" srcset="hero-large.jpg"> <!-- Fallback & required alt text --> <img src="hero-large.jpg" alt="Sunset over the city"> </picture>
Modern format with a fallback
<picture> <source srcset="photo.webp" type="image/webp"> <img src="photo.jpg" alt="Team photo" width="800" height="500"> </picture>
<img> inside is not optional. It's the fallback and the
place where alt text lives. A <picture> with no inner
<img> shows nothing.
Captions & accessibility
Media that only works with sound and sight shuts people out. A <track>
element adds captions to video; a transcript helps everyone. Here's what to get right.
<video controls> <source src="talk.mp4" type="video/mp4"> <!-- Captions load from a .vtt text file --> <track kind="captions" src="talk.vtt" srclang="en" label="English" default> </video>
Do
- Add a
<track>with captions for spoken video - Give every
<img>meaningfulalttext - Keep
controlsvisible so users can pause - Set
width&heightto prevent layout shift - Compress files — a hero video should be a few MB, not 70
- Offer a text transcript for podcasts & talks
Don't
- Autoplay video with sound — it startles users (and is blocked)
- Re-host someone else's YouTube video — embed it instead
- Ship a 100 MB uncompressed
.movstraight from your phone - Hide
controlswith no other way to pause - Rely on color or sound alone to carry meaning
- Forget the fallback text between the media tags
Everything at a glance
The four patterns condensed. Copy what you need into any project.
<video controls poster="cover.jpg" width="640"> <source src="clip.mp4" type="video/mp4"> </video>
<audio controls> <source src="episode.mp3" type="audio/mpeg"> </audio>
<div class="embed-16x9"> <iframe src="https://www.youtube.com/embed/VIDEO_ID" title="Video" allowfullscreen></iframe> </div>
<picture> <source srcset="photo.webp" type="image/webp"> <img src="photo.jpg" alt="Description"> </picture>