Published on

Web Performance - The Art of Media Optimization

Authors
  • avatar
    Name
    Abdullayev Shatlyk
    Twitter

Image Optimization

Web performance is critical for user satisfaction and engagement. One of the most effective ways to enhance website speed is through media optimization (images, video etc.).

Images often account for a significant portion of a webpage’s load time, so optimizing them can drastically improve performance. We’ll explore several key technique for image optimization.

Using srcset and loading attributes

The srcset attribute is a powerful tool that allows developers to provide multiple image sources tailored to different screen pixel ratios or device resolutions. By serving the most appropriate image size based on the user’s device, srcset reduces unnecessary data transfer, leading to faster page loads and a better user experience.

<img src="/bg-1.png" srcset="/bg-2x.png 2x" loading="lazy" alt="background image" />

Code breakdown

  • The src attribute specifies the default image source (/bg-1.png). This is the fallback image used by browsers that don’t support srcset or when no other conditions match.
  • The srcset attribute provides a comma-separated list of image sources along with their respective pixel density.
  • /bg-2x.png 2x is served for devices with higher pixel ratios. Here is 2x indicates using this image source for higher pixel density screens, e.g., for Retina displays.
  • loading="lazy" attribute enables lazy loading, a technique that defers the loading of images until they are about to enter the viewport. This reduces initial page load time by prioritizing content that is immediately visible to the user.

Why this important?

By offering different image resolutions, the browser selects the most appropriate image based on the device’s pixel density, reducing the loading of unnecessarily large files on lower-resolution screens.

Using picture and source elements (image)

The <picture> element is a modern HTML feature that allows developers to provide multiple image sources for different scenarios, enabling browsers to choose the most appropriate image based on factors like screen size, resolution, or device capabilities. This flexibility is key to delivering optimized images that balance quality and performance.

<picture>
  <source media="(max-width: 1440px)" srcset="large-bg-1x.png 1x, large-bg-2x.png 2x" />
  <source media="(max-width: 600px)" srcset="small-bg-1x.png 1x, small-bg-2x.png 2x" />
  <!-- fallback -->
  <img src="large-bg.png" alt="background image" />
</picture>

Code breakdown

  • The <picture> element acts as a wrapper that contains one or more <source> elements and optional fallback <img> element.

  • It gives the browser options to choose the best image source based on defined criteria, such as the viewport size or device characteristics.

  • The first <source> provides large-bg-1x.png 1x and large-bg-2x.png 2x for viewports less than 1440 pixels media="(max-width: 1440px)". Here are the sources with large images. Additionally, we're using srcset for various pixel densities.

  • The second <source> provides small-bg-1x.png 1x and small-bg-2x.png 2x for viewports 600 pixels or less (media="(max-width: 600px)"). Here are the sources with small images. Additionally, we're using srcset for various pixel densities.

  • The browser reads (from top to bottom) the source element conditions and selects the first matching <source> element to load the appropriate image. If no condition satisfies, the fallback <img/> will be used.

Why this important?

  • By serving smaller images to devices with smaller viewports, we reduce the amount of data transferred. Smaller images mean faster load times, especially on mobile devices with limited bandwidth.

  • Delivering appropriately sized images ensures that users see visuals tailored to their device’s screen size and resolution.

  • Faster-loading pages improve key web performance metrics like Largest Contentful Paint (LCP) and First Contentful Paint (FCP), which are part of Google’s Core Web Vitals. Better scores can boost your site’s search engine rankings.

  • The <picture> element supports art direction, allowing you to serve different image compositions (e.g., a close-up portrait for mobile vs. a wider shot for desktop). This ensures the image is visually optimized for each context, enhancing both aesthetics and performance.

See browser supports for <picture> element.

Why Build in components might be bad choice?

Why should I use the <picture> element for image optimization when built-in components like Next Image already provide image optimization features?

Next.js’s <Image> component, are tightly integrated with specific hosting platforms like Vercel. This convenience comes at a cost:

  • Platforms like Vercel impose limits on image optimization usage, especially on free or hobby tiers. For example, Vercel’s free tier caps optimized images at 1,000 per month, which can be quickly exhausted for image-heavy sites, leading to broken images or the need to upgrade to a paid plan.

  • Relying on a platform’s image optimization service can make it harder to migrate to another hosting provider without losing the benefits of automated optimization or needing to reconfigure your setup.

  • Potential for Over-Optimization: Automated tools may overly compress images, leading to a noticeable loss in quality that can harm the visual appeal of your site, especially for high-resolution displays. Similarly, Astro’s default image optimization with Sharp may not allow for the same level of customization as manual optimization pipelines

Tips for <picture/> element

To maximize the benefits of the <picture> element, use these tips:

  • Use tools like TinyPNG, or Squoosh to compress images without losing quality. Here you can find good resources

  • Test Responsiveness: Use browser developer tools to simulate different screen sizes and ensure the correct image is loaded.

Video optimization + code snippet

Optimizing video delivery enhances web performance by reducing load times and bandwidth usage.

Using <video/> and <source/> elements

The <video> element with <source> tags allows browsers to select the first compatible video format. Make sure to list <source> elements from smallest to largest file size so the browser could prioritize the most efficient version.

<video muted autoplay loop playsinline>
  <!-- Smallest: Mobile -->
  <source media="(max-width: 480px)" src="videos/mobile.webm" type="video/webm" />
  <source media="(max-width: 480px)" src="videos/mobile.mp4" type="video/mp4" />

  <!-- Medium: Tablet -->
  <source media="(min-width: 481px)" src="videos/tablet.webm" type="video/webm" />
  <source media="(min-width: 481px)" src="videos/tablet.mp4" type="video/mp4" />

  <!-- Largest: Desktop (1080px) -->
  <source media="(min-width: 1200px)" src="videos/desktop.webm" type="video/webm" />
  <source media="(min-width: 1200px)" src="videos/desktop.mp4" type="video/mp4" />

  <!-- Fallback content -->
  <p>Your browser does not support video playback.</p>
</video>

Code breakdown

  • muted attribute ensures the video plays without sound, required for autoplay in most browsers.

  • loop attribute repeats the video continuously

  • playsinline attribute allows inline playback on mobile devices

  • autoplay attribute enables automatic playback of media content.

  • The browser selects the first <source> it supports that matches the media query, ensuring smaller files are prioritized for smaller screens.

  • media attribute control which video is loaded based on viewport size.

In the example above, we prioritize .webm over .mp4 because the .webm format is generally smaller and more efficient

NOTE

To enable automatic video playback in browsers, ensure the video is muted (ex. with muted attribute). Otherwise, playback will start only after user interaction with the site.

See chrome autoplay policy for more info

Video Resizing with matchMedia Api (code snippet)

The following code snippet allows you to dynamically load the appropriate video source based on viewport size using HTML <source> elements and JavaScript media query listeners for optimal performance and responsiveness.

JS code

function handleVideoResize(videoElement) {
  const sources = Array.from(videoElement.getElementsByTagName('source'))

  const updateVideo = () => {
    videoElement.load()
  }

  updateVideo()

  const mqls = sources
    .filter((source) => source.media)
    .map((source) => {
      const mql = window.matchMedia(source.media)
      mql.addEventListener('change', updateVideo)
      return mql
    })

  return () => {
    mqls.forEach((mql) => mql.removeEventListener('change', updateVideo))
  }
}

JSX code

import { type RefObject } from "react"

export const handleVideoResize = (videoRef: RefObject<HTMLVideoElement | null>) => {
  const sources = Array.prototype.slice.call(
    videoRef.current?.getElementsByTagName("source") || [],
  ) as HTMLSourceElement[]
  const updateVideo = () => {
    videoRef.current?.load()
  }

  updateVideo()

  const mqls: MediaQueryList[] = sources
    .filter((source) => source.media)
    .map((source) => {
      const mql = window.matchMedia(source.media)
      mql.addEventListener("change", updateVideo)
      return mql
    })

  return () => {
    mqls.forEach((mql) => mql.removeEventListener("change", updateVideo))
  }
}

How it works?

  • Retrieves all <source> elements from the videoRef

  • Triggers video.load() to select the appropriate source based on current media query matches.

  • Sets up matchMedia listeners for each source’s media attribute to reload the video when the viewport size changes.

The matchMedia API dynamically monitors viewport changes, and calling video.load() ensures the browser selects the <source> that matches the current media query. This approach avoids manual source switching and provides a seamless, responsive video experience.

Summary

  • Image Optimization: Use srcset to show the right image size for different devices and loading to delay loading off-screen images. The <picture> element helps pick the best image for the screen size to speed up page loads.

  • Issues with Built-in Tools: Tools like Next.js’s <Image> component can have limits, like image caps or less control, so manual optimization is often better (IMHO).

  • Tips: Use tools like TinyPNG or Squoosh to shrink images, test how images look on different screens, and check browser support for better performance.

  • Video Optimization: Use <video> and <source> to choose smaller video files for smaller screens, like .webm instead of .mp4. The matchMedia API helps switch videos when the screen size changes.