HTML
09 / 12

Media & Embedded Content

HTML: Media & Embedded Content

Images — Responsive & Performant

<!-- Basic image with required alt -->
<img src="photo.jpg" alt="Alice presenting at a conference">

<!-- Decorative image — empty alt, not described to screen readers -->
<img src="decorative-line.svg" alt="">

<!-- Responsive images — let browser choose optimal size -->
<img
  src="hero-800.jpg"
  srcset="
    hero-400.jpg  400w,
    hero-800.jpg  800w,
    hero-1200.jpg 1200w,
    hero-1600.jpg 1600w
  "
  sizes="
    (max-width: 600px) 100vw,
    (max-width: 1200px) 50vw,
    800px
  "
  alt="Hero image"
  width="1200"
  height="600"
  loading="lazy"
  decoding="async">

<!-- Always include width/height to prevent layout shift (CLS) -->
<!-- loading="lazy" defers offscreen images until near viewport -->
<!-- decoding="async" doesn't block main thread during image decode -->

picture Element — Art Direction

<!-- Different image crops for different screen sizes -->
<picture>
  <!-- Mobile: tall portrait crop -->
  <source
    media="(max-width: 600px)"
    srcset="hero-mobile.webp"
    type="image/webp">

  <!-- Tablet: square crop -->
  <source
    media="(max-width: 1024px)"
    srcset="hero-tablet.webp"
    type="image/webp">

  <!-- Desktop: wide landscape -->
  <source srcset="hero-desktop.webp" type="image/webp">

  <!-- Fallback for browsers not supporting WebP -->
  <img src="hero-desktop.jpg" alt="Product hero image" width="1440" height="600">
</picture>

<!-- Modern format with JPEG fallback -->
<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" alt="A beautiful landscape">
</picture>

Video

<video
  controls
  width="800"
  height="450"
  poster="thumbnail.jpg"
  preload="metadata"    <!-- none | metadata | auto -->
  playsinline           <!-- plays inline on iOS, not fullscreen -->
  loop
  muted                 <!-- required for autoplay to work in most browsers -->
  autoplay>

  <source src="video.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">

  <!-- Subtitles / closed captions (required for accessibility) -->
  <track kind="subtitles" src="captions.vtt" srclang="en" label="English" default>
  <track kind="subtitles" src="captions-uk.vtt" srclang="uk" label="Ukrainian">

  <p>Your browser doesn't support HTML video.
    <a href="video.mp4">Download the video</a></p>
</video>

Audio

<audio controls preload="none">
  <source src="podcast.opus" type="audio/ogg; codecs=opus">
  <source src="podcast.mp3" type="audio/mpeg">
  <p>Your browser doesn't support HTML audio.</p>
</audio>

<!-- Autoplay audio is blocked by browsers unless muted -->
<audio autoplay muted loop>
  <source src="ambient.mp3" type="audio/mpeg">
</audio>

iframe — Embedding External Content

<!-- Embed a YouTube video (privacy-enhanced mode) -->
<iframe
  width="560"
  height="315"
  src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
  title="Video title"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin">
</iframe>

<!-- Sandbox — restricts iframe capabilities -->
<iframe
  src="https://example.com/widget"
  sandbox="allow-scripts allow-same-origin allow-forms"
  title="Widget description">
  <!-- sandbox values (additive):
    allow-scripts        — run JavaScript
    allow-same-origin    — maintain origin (for cookie access, localStorage)
    allow-forms          — submit forms
    allow-popups         — window.open()
    allow-top-navigation — navigate parent frame
  -->
</iframe>

canvas Basics

<canvas id="myCanvas" width="800" height="400">
  <!-- Fallback content for non-canvas browsers -->
  Your browser doesn't support Canvas.
</canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  // Shapes
  ctx.fillStyle = '#0070f3';
  ctx.fillRect(10, 10, 100, 50);  // x, y, width, height

  ctx.strokeStyle = '#ff0000';
  ctx.lineWidth = 3;
  ctx.strokeRect(120, 10, 100, 50);

  // Path
  ctx.beginPath();
  ctx.moveTo(10, 100);
  ctx.lineTo(100, 150);
  ctx.lineTo(50, 200);
  ctx.closePath();
  ctx.fill();

  // Text
  ctx.font = '24px sans-serif';
  ctx.fillStyle = '#000';
  ctx.fillText('Hello Canvas', 10, 300);

  // Image
  const img = new Image();
  img.onload = () => ctx.drawImage(img, 200, 10, 200, 100);
  img.src = 'photo.jpg';
</script>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free