Plate 1.4 · Phase 1 — Framing · 25 minutes
Images, video and file paths
Pictures make a page feel alive, and they are also the single heaviest thing on almost every website. This module is about putting media on a page so it loads fast, never shows up broken, and still makes sense to someone who can't see it.
Warm-up · from Module 1.3You write <a href="www.nps.gov">. Where does clicking it take you?
To a file called www.nps.gov inside your own site — which doesn't exist. Links to other sites must start with https://.
Why images deserve a whole module
Go back to the Network tab exercise from Module 0.4 and sort by size: on nearly every site, the heaviest files are images. A single photo straight off an iPhone is 3–5 MB — more than the entire HTML, CSS and JavaScript of most pages combined. Get images wrong and your site is slow, broken, or silent to blind visitors. Get them right and none of that happens, for about two minutes of extra work per image.
The image element
<img src="images/rainier.jpg"
alt="Mount Rainier at sunrise,
snow-covered above a pine forest"
width="1600" height="900">
A void element — no closing tag, as you learned in 1.1 — with four attributes that each do a real job:
| Attribute | Job | Leave it out and… |
|---|---|---|
| src | Which file. A path, following every rule from Module 0.3. | No image. Nothing to show. |
| alt | A text replacement for anyone who can't see the image. | Screen readers read out the filename — "rainier underscore final two dot jpg". |
| width / height | The image's real proportions, so the browser can reserve the space before it downloads. | The page jumps as each image arrives — text you were reading slides away. |
Alt text: the museum label read aloud
At the Louvre, a guide walking a blind visitor through the galleries doesn't say "a painting, oil, 77 by 53 centimeters." They say what matters: a woman with a faint smile, hands folded, a winding landscape behind her. They describe what the image is for in that room. That is exactly the job of alt.
Alt text is also what appears when the image fails to load — slow connection, wrong path, blocked file. Try it:
The same image tag, with a working file path and with a broken one.
| State | The src | What the visitor gets |
|---|---|---|
| File found | images/rainier.webp | A 480 by 270 picture of a snow-covered mountain at sunrise. |
| File missing | images/Rainier Final.JPG | No picture. The browser prints the alt text in its place: "Mount Rainier at sunrise, snow-covered above a pine forest". |
<img src="images/rainier.webp"
alt="Mount Rainier at sunrise,
snow-covered above a pine forest"
width="480" height="270">Notice that the alt text does its work either way: it is what a screen reader says, and what everyone sees when the file fails to load. Width and height keep the space reserved while the image loads.
For a screen reader user, the alt text is the image.
When the file is missing, the words take its place — every single time. Describe what the image is for on this page; leave alt="" empty only when the image is pure decoration.
The four rules of alt text
- Describe the purpose, not just the pixels. A photo of a Boston clam chowder on a menu page: "New England clam chowder in a bread bowl." On a page about bread bowls: "A sourdough bread bowl, hollowed and filled with chowder." Same photo, different job.
- Never start with "image of" or "picture of". The screen reader already announces it's an image.
- Decorative images get an empty alt:
alt="". A flourish, a background texture, a divider. Empty tells a screen reader to skip it. Missing alt makes it read the filename — the worst of both. - If an image is a link, describe where it goes. A logo linking home:
alt="Cedar & Pine Coffee — home", not "logo".
Write better alt text
Choosing a file format
| Format | Best for | Avoid for |
|---|---|---|
| JPEG .jpg | Photographs. Small files, universal support. | Logos and text — edges go fuzzy. |
| PNG .png | Screenshots, graphics with transparency. | Photos — often five times the size of a JPEG. |
| WebP .webp | Photos and graphics, roughly 25–35% smaller than JPEG. Every modern browser supports it. | Nothing much. A strong default. |
| AVIF .avif | Even smaller photos than WebP. | Older browsers — offer a JPEG fallback (a backup choice the browser uses when it can't handle the first one) with <picture> (below). |
| SVG .svg | Logos, icons, simple illustrations. Drawn with math, so razor sharp at any size, often a few KB. | Photographs — impossible. |
| GIF .gif | Almost nothing today. | Animations — a short muted video is a tenth of the size. |
Size: the one habit that makes sites fast
Resize before you upload. An iPhone photo is around 4000 pixels wide. The widest your content area will ever be is roughly 1200–1600. Everything beyond that is downloaded, decoded, and thrown away — on every visit, by every visitor.
| The same photo | Size | On a slow phone connection |
|---|---|---|
| Straight off the phone | 4032 px JPEG · ~4 MB | Several seconds of blank space |
| Resized | 1600 px JPEG · ~350 KB | Under a second |
| Resized + WebP | 1600 px WebP · ~180 KB | Near instant |
Free tools do this in seconds: Squoosh (squoosh.app, from Google, runs in your browser — it resizes and converts to WebP), or Preview on a Mac (Tools → Adjust Size) for resizing. Make it a reflex: resize, compress, then upload.
iPhones usually save photos as HEIC, a format Chrome and Squoosh can't open. Either set Settings → Camera → Formats → Most Compatible for future photos, or export the photo as JPEG first (in the Photos app on a Mac: File → Export, choose JPEG). Then resize.
Later you'll go further: <picture> lets you offer a modern format with a fallback, and srcset lets a phone download a smaller version than a desktop (Module 8.2 covers both). A preview of the shape:
<picture>
<source srcset="images/rainier.avif" type="image/avif">
<img src="images/rainier.jpg"
alt="Mount Rainier at sunrise…"
width="1600" height="900">
</picture>
One more free speed win: add loading="lazy" to images further down the page. The browser then only fetches them as the visitor scrolls near. Don't use it on the first image people see — that one should load immediately.
Images with captions
When an image needs a visible caption — a credit, a date, an explanation — wrap both in <figure>, and the caption in <figcaption>. That ties them together for assistive technology rather than leaving a loose paragraph that merely happens to sit nearby.
<figure>
<img src="images/golden-gate.webp"
alt="The Golden Gate Bridge in fog, towers just visible"
width="1600" height="900" loading="lazy">
<figcaption>
San Francisco, July — photo by Dana Whitfield.
</figcaption>
</figure>
Alt and caption do different jobs: alt replaces the image for people who can't see it; the caption adds information for everybody. Don't copy one into the other.
Video and audio
<video controls width="1280" height="720"
poster="images/roastery-poster.webp">
<source src="media/roastery.mp4" type="video/mp4">
<track kind="captions" src="media/roastery-en.vtt"
srclang="en" label="English">
Your browser can't play this video.
<a href="media/roastery.mp4">Download it instead</a>.
</video>
controlsshows play, pause, volume and full-screen. Without it, people can't operate the video at all.posteris the still image shown before playback — otherwise it's often a black rectangle.<track kind="captions">adds subtitles. Deaf visitors need them, and so does everyone watching on a train with the sound off — most social video is watched muted.- Never autoplay with sound. Browsers block it anyway. A silent background loop is allowed:
autoplay muted loop playsinline— but anything that moves for more than five seconds needs a way to pause it (an accessibility rule, WCAG 2.2.2), so keepcontrolsor keep the loop short.
<audio controls> works exactly the same way, for podcasts and music.
Video files are enormous, and serving them yourself can blow through a free hosting plan in an afternoon. For anything longer than a short loop, upload to YouTube or Vimeo and embed their player with the <iframe> code they give you. They handle the streaming, the compression for every device, and the bandwidth bill.
Three things beginners get wrong
1. Leaving out alt. Blind visitors hear filenames, and Google has nothing to go on. Every <img> gets an alt — descriptive, or empty if decorative. Never missing.
2. Uploading photos straight from the phone. Four megabytes each, ten per page, and a site that crawls on mobile data. Resize and compress first, every time.
3. Paths and capitals. Images/Photo.JPG works on your laptop and breaks live. It's Module 0.3 again — and it's the most common broken image on the internet.
Words you now own
- src
- The path to the media file.
- alt text
- The text replacement for an image, read by screen readers and shown when it fails.
- decorative image
- One that adds nothing to meaning. Gets
alt="". - layout shift
- Content jumping as images load. Prevented with width and height.
- WebP / AVIF
- Modern formats, much smaller than JPEG for the same quality.
- SVG
- A vector format for logos and icons — sharp at any size.
- lazy loading
- Fetching an image only as the visitor scrolls near it.
- figure / figcaption
- An image and its visible caption, bound together.
- poster
- The still image shown before a video plays.
- captions track
- A
.vttsubtitle file attached with<track>.
Put real pictures on your site — properly.
What you should see — one way to do it
The two images
<img src="images/maria-hiking.webp"
alt="Maria on the summit trail at Mount Tamalpais,
smiling at the camera"
width="800" height="600">
<figure>
<img src="images/first-website-sketch.webp"
alt="A pencil sketch of my site's home page layout"
width="800" height="1000" loading="lazy">
<figcaption>
My first sketch of this site, drawn before writing any code.
</figcaption>
</figure>
Use your image's real width and height — Squoosh shows them after resizing.
Network tab
Filter by Img. Your photo's Size column should read somewhere around 40–250 kB. If it says 3 MB, the resized file isn't the one your code is pointing at.
The YouTube iframe, decoded
src— the player's address for that video.width/height— reserved space, like on an image.title— what a screen reader announces for the frame. Change YouTube's generic title to the video's real name.allow— which powers the player gets (autoplay, fullscreen, picture-in-picture…).allowfullscreen— lets the full-screen button work.
Six questions. Four correct to move on.
"Every image gets alt — descriptive, or empty if decorative. Width and height stop the jumping. Resize before upload. Photos are WebP or JPEG, logos are SVG. Big video lives on YouTube." That's the whole of media done properly, and most of the web still gets it wrong.