Video, audio and embedding somebody else's page
Sooner or later somebody will ask you to put a video on the page, or a Google Map showing where the shop is. Both are easy. Both have a cost that beginners do not see until the site is live on somebody's mobile data.
Video
<video controls width="800" height="450" poster="shop-poster.jpg" preload="metadata">
<source src="shop-tour.webm" type="video/webm">
<source src="shop-tour.mp4" type="video/mp4">
<track kind="captions" src="shop-tour.vtt" srclang="en" label="English" default>
<p>Your browser cannot play this video.
<a href="shop-tour.mp4">Download it instead</a>.</p>
</video>
Each attribute is doing something worth knowing.
controls shows the browser's own play button, timeline and volume. Leave it
out and there is no way to play the video unless you write JavaScript. Beginners
leave it out and then wonder why nothing happens when they click.
poster is the still image shown before it plays. Without one you get a black
rectangle, or in some browsers the first frame, which is frequently a blur.
preload="metadata" is the setting to use. The default (auto) lets the
browser start downloading the video before anybody has asked for it, which on a
phone can mean several megabytes of somebody's data plan spent on a video they
never play. metadata fetches only enough to know the duration. none fetches
nothing at all and is right if the video is far down the page.
Two <source> elements. The browser uses the first it can play. WebM first
because it is smaller; MP4 as the fallback that plays everywhere.
<track kind="captions"> is the accessibility requirement, and unlike most
of them it takes real work — somebody has to write the caption file. Without it,
your video is unavailable to anybody deaf or hard of hearing, and to the much
larger group watching with the sound off because they are on a bus.
The <p> at the end shows only if the element is unsupported.
Autoplay, and why it mostly does not work
<video autoplay muted loop playsinline>
Browsers block autoplay with sound. All of them. So autoplay without muted
simply does nothing, which is the most common confusion with this element. The four
together are the recipe for a silent background video, and playsinline is needed
or iOS takes it fullscreen.
Ask whether you want it at all. A looping background video is a few megabytes running continuously, it drains battery, and it is precisely the sort of thing that makes a site unusable on a ₹200-a-month data plan. If you must, gate it:
@media (prefers-reduced-motion: reduce) {
video { display: none; }
}
Audio
<audio controls preload="none">
<source src="jingle.mp3" type="audio/mpeg">
<p>Your browser cannot play this audio.</p>
</audio>
Same rules, smaller files. And never autoplay audio. There is no situation in
which a visitor wants sound to start by itself.
Hosting video yourself versus embedding
This is the real decision, and it is usually made without thinking.
Self-hosted (<video> with your own file): you control it completely, there
are no third-party cookies, and nobody is tracked. But you serve the bytes, and
you get no adaptive quality — a visitor on a poor connection gets the same
file as everybody else and it stalls.
YouTube or Vimeo embed: adaptive streaming, their bandwidth, their player. But an embed is roughly a megabyte of JavaScript before a single frame plays, it sets cookies, and it makes your page slower in a way that shows up immediately in Lighthouse.
For a two-minute shop tour, self-host. For anything long, embed.
<iframe>
An iframe puts another page inside yours — a map, a video player, a payment form.
<iframe
src="https://www.google.com/maps/embed?pb=…"
width="600" height="450"
style="border:0"
loading="lazy"
title="Map showing Sharma Kirana on Karve Road"
referrerpolicy="no-referrer-when-downgrade"
allowfullscreen></iframe>
title is required for accessibility, and it is the attribute everybody
forgets. A screen reader announces an iframe as a frame and reads the title. With
no title the user hears "frame" and nothing else — they cannot tell whether it is
worth entering.
loading="lazy" matters more here than on images, because an iframe loads a
whole page. A map below the fold with no lazy attribute costs every visitor a full
page load they may never scroll to.
sandbox, for anything you do not control
<iframe src="https://example.com/widget" sandbox title="…"></iframe>
sandbox with no value is maximum restriction: the framed page cannot run scripts,
submit forms, or navigate your page. Then add back only what it needs:
<iframe src="…" sandbox="allow-scripts allow-same-origin" title="…"></iframe>
Be aware that allow-scripts allow-same-origin together, on a page from your own
origin, effectively removes the sandbox — the framed page can reach out and remove
its own restrictions. For third-party content that combination is the one to think
twice about.
The general principle: an iframe is somebody else's code running on your page. It cannot read your page's content — the browser prevents that — but it can set cookies, track visitors, and slow you down. Embed only what you need.
The lazy-embed pattern
For a heavy embed, show a picture and load the real thing only when somebody asks:
<a href="https://maps.google.com/?q=Sharma+Kirana+Karve+Road">
<img src="map-static.png" width="600" height="450"
alt="Map showing Sharma Kirana on Karve Road, Kothrud">
</a>
A static image and a link. No third-party JavaScript, no cookies, loads instantly, and tapping it opens the map app on a phone — which is what the visitor wanted anyway. For a small business site this is very often the better answer than a real embed, and almost nobody does it.
Responsive embeds
An iframe has fixed width and height attributes, so it overflows a phone
screen. The modern fix is one property:
.embed {
aspect-ratio: 16 / 9;
width: 100%;
}
.embed iframe {
width: 100%;
height: 100%;
border: 0;
}
aspect-ratio is well supported now and replaces the old padding-percentage hack
you will still find in tutorials. If you see padding-bottom: 56.25% anywhere,
that is what it was working around.
<picture>, for real control over images
Related, and worth having here: <picture> lets you serve different files to
different situations.
<picture>
<source srcset="shop.avif" type="image/avif">
<source srcset="shop.webp" type="image/webp">
<img src="shop.jpg" alt="The shopfront on Karve Road" width="1200" height="800">
</picture>
The browser takes the first <source> it understands and falls back to the <img>.
Note that the <img> is required — it is what actually renders, and it carries
the alt, the width and the height.
For different sizes rather than different formats, srcset on the <img> alone
does the job, and module 4 covers it properly.
Check your work
What controls does. Provides the play button. Without it and without
JavaScript, the video cannot be played.
Why preload="metadata". The default can download megabytes before anybody
presses play.
What poster prevents. A black rectangle or a blurred first frame.
Why two <source> elements. WebM for size, MP4 as the universal fallback.
Why autoplay alone does nothing. Browsers block autoplay with sound; it needs
muted, and playsinline for iOS.
What captions are for. Deaf and hard-of-hearing visitors, and everybody with the sound off.
Self-host versus embed. Control and privacy versus adaptive streaming, at the cost of about a megabyte of JavaScript.
The iframe attribute everybody forgets. title — without it a screen reader
says only "frame".
Why loading="lazy" matters more on an iframe. It loads an entire page.
What sandbox with no value means. Maximum restriction; add capabilities back
one at a time.
The lazy-embed pattern. A static image linking out — faster, private, and often what the visitor wanted.
What aspect-ratio replaced. The padding-bottom: 56.25% hack.
Why <picture> still needs an <img>. That is what renders and carries the
alt, width and height.
Practice
- Put a short video on a page with
controls, aposterandpreload="metadata". - Remove
controlsand try to play it. - Set
preload="auto", throttle to Slow 4G, and check in the Network tab how much downloads before you press play. Compare withmetadataandnone. - Add
autoplaywithoutmuted. Then addmuted. - Write a
.vttcaption file with three captions and attach it with<track>. - Embed a Google Map with a
titleandloading="lazy". Check the Network tab for how many requests the embed makes. - Remove the
titleand listen to the iframe with a screen reader. - Replace that embed with the static-image-and-link pattern. Compare the total page weight and the load time.
- Add
sandboxto an iframe and see what stops working. - Open a page with a fixed-width iframe at 360px and note the horizontal scroll.
Fix it with
aspect-ratio. - Build a
<picture>with AVIF, WebP and JPEG. In the Network tab, confirm which one your browser fetched — and that it fetched only one. - Find a real site with a YouTube embed and count its requests in the Network tab.
Official documentation
- MDN — The video element — Every attribute, and the full list of what
preloaddoes. - MDN — Media container and codec guide — Which formats to ship and why, in far more depth than this lesson.
- MDN — iframe sandbox — Each
sandboxtoken and the combinations that cancel each other out.
Next: two elements that give you interactive components with no JavaScript at all.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship