Making Sense of Google's Web Fundamentals, Part 2


This post is a continuation of part one in a series that summarizes Google's Web Fundamentals.

Web Push Notifications

The widely available Push API allows websites to send browser-based messages that look and behave like notifications from native mobile apps.

  • Since service workers run in a non-blocking background thread, they are responsible for processing push notifications.
  • The 3 key steps to implementing push notifications are:
    1. Subscribe a user (from the browser)
    2. Trigger a message (from the API server)
    3. Receive a push message (from the browser)
  • Subscribing a User
    1. Check that the browser supports push messaging:
              if (!('serviceWorker' in navigator)) {
              if (!('PushManager' in window)) {
    2. Register a Service Worker
    3. Request permission using Notification.requestPermission()
    4. Subscribe the user using PushManager.subscribe()
    5. Send the subscription to your custom server api (via ajax)
  • Sending push notifications
    • The web push protocol is "fiddly," and Google recommends using a 3rd-party library like node's web-push
    • Here are more details on the Web Push Protocol
  • Receiving push events
    1. In the service worker, listen for the push event.
    2. Show the push message by calling ServiceWorkerRegistration.showNotification()
  • Handling user interaction with a push message
    • Listen to the notificationclick or notificationclose events within the service worker.
  • General Info for Push Notifications
    • The browser won't send push notifications if it is completely closed
    • Push notifications usually don't open the web page in full-screen mode when a website is saved to the home screen in mobile.
    • Versus websockets: push notifications will work even if the site is not open in a browser window.
    • Troubleshooting errors with push can be tricky. Google provides this general troubleshooting guide

Video and Audio Playback

Video and audio are essential media formats for the web. However, because of differences between platforms and connection speeds, serving these formats can be a challenge.

  • An example video tag:
        <video controls>
          <source src="" type="video/webm">
          <source src="" type="video/mp4">
          <p>This browser does not support the video element.</p>
  • The code above specifies the different formats using the source element.
  • Ensure cross-browser support by providing multiple video formats
  • Always include the correct type attribute for each source element
  • Consider setting start and end times to videos using the Fragment URI (#t=[start_time][,end_time]). Example:
          <source src="video/chrome.webm#t=5,10" type="video/webm">
  • Include a poster image using the poster attribute on the video element
  • Use a tool like FFmpeg to convert videos to different formats.
  • Ensure videos are as short as possible
  • Videos should follow the same responsive design principals as images. This guide shows how to handle both HTML5 and iframe (i.e., YouTube) videos responsively.
  • Consider using the Fullscreen API to give users an additional viewing option.
  • For accessibility, provide video captions
  • Autoplay
    • Specified with the autoplay attribute
    • Does not always work on mobile
    • Use with caution, since it can make data usage expensive
  • Refer to this HTML5Rocks page for detailed info on all properties and methods of the video element.
  • Consider video preload for faster playback.

Media Capture

The ability to capture media, including video, audio, and photos, is supported in most major browsers. However, the capture behavior varies significantly between these browsers.

Audio Capture

  • Basic file input with audio capture:
        <input type="file" accept="audio/*" capture>
  • The code above will work on all platforms.
    • For desktop, users will interact with the file input and capture won't be triggered.
    • For mobile, it will first prompt the user with a microphone app to record a new audio file. Then, the newly created file will be selected.
    • Once the audio file is selected, it can be played or manipulated via javascript. Example:
              <input type="file" accept="audio/*" capture id="recorder">
              <audio id="player"controls></audio>
                var recorder = document.getElementById('recorder');
                var player = document.getElementById('player');
                recorder.addEventListener('change', function(e) {
                  var file =[0];
                  // Do something with the audio file.
                  player.srcObject = file;
    • To access the microphone interactively, use MediaDevices.getUserMedia()
    • After accessing the microphone, you handle its streaming data by using the Web Audio API.

Image Capture

  • Different techniques for capturing images:
    • Ask for URL
      • Users input the URL to the image. It is then displayed in the DOM using Javascript
      • Because of CORS restrictions, however, this file cannot be manipulated in the browser
    • File input
      • Provide a type="file" input where the user will select a file. Then, by listening for the element's change event in Javascript, you can access the file. Example:
                    <input type="file" accept="image/*" id="file-input">
                      const fileInput = document.getElementById('file-input');
                      fileInput.addEventListener('change', (e)=>doSomethingWithFiles(;
      • In mobile, the file input will allow the user to select a file from native sources like the photo library or camera
      • Also for mobile, if the input has a capture element, the user will be prompted first with the camera
    • Drag and Drop
      • In addition to using the file input, the desktop user experience can be improved with file drag and drop
  • The Javascript FileList object:
    • FileList is an array-like object containing a list of File objects. The File object contains both the file metadata (name and lastModified) and blob content
    • File drag/drop and the file input both work with FileList, rather than a single file, even if only one file is selected
  • For more in-depth code samples related to interacting with the camera, check out the last sections in Capturing Images on Web Fundamentals

Video Capture

  • Basic file input with video capture:
          <input type="file" accept="video/*" capture>
  • The code above will work on all platforms.
    • For desktop, users will interact with the file input like normal and capture won't be triggered.
    • For mobile, it will first prompt the user with a camera app to record a new video. Then, the newly created file will be selected.
    • Assign the capture attribute to "user" to select the user-facing camera or to "environment" to select the outward facing camera
    • Once the video file is selected, it can be played or manipulated via javascript. Example:
              <input type="file" accept="video/*" capture="camera" id="recorder">
              <video id="player" controls></video>
                var recorder = document.getElementById('recorder');
                var player = document.getElementById('player');
                recorder.addEventListener('change', function(e){
                  var file =[0];
                  // Do something with the video file.
                  player.src = URL.createObjectURL(file);
    • For more in-depth code samples for interacting with the camera for video, check out the last sections in the Capturing Video page of Web Fundamentals

Manipulating Media Files

  • Multiple streams: a media file can contain more than one stream. For example, a video can have both "video" and "audio" streams.
  • Common terms:
    • Bitrate: Max number of bits/second in a stream. Higher bitrate usually means higher quality
    • Resolution: The number of pixels for each dimension in a video frame. 1080p means 1080 horizontal pixels.
    • Codec: a.k.a. "coder-decoder", is the compression format for audio and video. Different from file format. If file format is the "container," then codec arranges the data in the container. File formats can support multiple codecs.
  • Use Shaka Packager or FFMpeg for media processing.
  • For more in-depth information on preparing raw media for the web, check out From Raw Video to Web Ready or Media Manipulation Cheat Sheet on Web Fundamentals

Web Virtual Reality (WebVR)

This information is outdated on Web Fundamentals. The new standard for VR, WebXR API, is currently under development. It has poor browser support, so let's skip it for now.


Improvements in web technology have allowed sites to push the boundaries in terms of functionality. However, often this functionality comes with a performance cost.

Performance is about retaining users

Performance is about improving conversions

Website speed is crucial to business revenue.

  • Use the Impact Calculator to get a sense of potential business opportunities related to your site's performance.

Performance is about the user experience

  • On slower devices or connection speeds users may experience a much greater lag in page loads and overall responsiveness than on high-powered desktop computers with fast broadband connections and beefy hardware
  • Web performance is complex, and analyzing it involves capturing an array of metrics under many different conditions. Performance tools help to handle this complexity and provide the proper analysis coverage.
    Some key points on testing tools:
    • Strengths
      • Helpful for debugging performance issues
      • End-to-end and deep visibility into the UX
      • Reproducible testing and debugging environment
    • Limitations
      • Might not capture real-world bottlenecks
      • Cannot correlate against real-world page KPIs

Performance is about people

  • With affordable cellular data plans worldwide, internet access has grown from luxury to necessity
  • At the same time, page size continues to rise
  • User benefits of faster sites:
    • Saving users money by reducing the number of bytes used for their data plans
    • Increased reliability on critical (and sometimes life-saving) functionality for businesses such as hospitals, clinics, and crisis centers

Mind what resources you send

After auditing your site for resources, you can use the criteria below as a good starting point to improving performance:

  • For CSS, avoid bloated libraries like Bootstrap. CSS libraries often load much more code than you need.
    NOTE: This is a bit of a disingenuous blanket statement. You can often reduce the amount of CSS from libraries by using tools like SASS and UnCSS. (I have achieved 100% lighthouse scores using Bootstrap in this fashion.)
  • For Javascript, avoid large libraries like jQuery. With new DOM API methods like querySelector(), jquery isn't as important these days.
    (NOTE: Google isn't necessarily suggesting not using libraries at all. It is a good idea to weigh the benefits of using a library against the costs in overall bundle size.
  • Consider using traditional multi-page sites, rather than SPAs, for scenarios where high functionality is not needed. SPAs typically require more javascript, and javascript is the most expensive resource on the web.

Mind how you send resources

Mind how much data you send


RAIL stands for "Response, Animation, Idle, Load."

  • RAIL goals versus guidelines:
    • Goals: key performance metrics related to user experience. These are based on human perception and thus are unlikely to change.
    • Guidelines: Recommendations that help you achieve those goals. These can change depending on hardware and network speed

User perception of performance delays

Time range (milliseconds) User perception
0-16ms UI animations need to run at least 60 frames per second for users to perceive them as smooth. That means animations should take no more than 16ms to run in each frame. The 16ms includes 6ms needed for the browser to paint a new frame.
0-100ms If your site responds within 100ms of a user action, then the user perceives it as responsive. (An example of this would be a user clicking a "buy now" button and the "loading" state appearing for that button within 100ms.)
100-300ms Users perceive a slight delay
300-1000ms Users still perceive a natural and continuous progression of tasks
10000ms or more Beyond 10000 milliseconds (10 seconds), users are frustrated and are likely to abandon tasks. (And they may never come back.)

Below is the breakdown of the RAIL guidelines:

  • R - Response
    • Users should see a response within 50 milliseconds. This applies to most user input actions, such as clicking buttons, toggling controls or starting animations. (It does not apply to touch drags or scrolls.)
  • A - Animation
    • Produce each frame in an animation in 10ms or less to ensure the site achieves the 60 FPS needed for smoothness.
  • I - Idle
    • Maximize the idle time after a user action (such as a button click). Tasks running during idle time should take no longer than 50ms to ensure a maximum total response time to 100ms. For example, load only the crucial data to perform the task after, then load the rest in the background.
  • L - Load
    • For initial page loads, strive for 5 seconds or less to fully load and become interactive for slower mobile connections and devices
    • For subsequent loads (i.e., fetch requests) aim for under 2 seconds to fully load and become interactive
    • Test your load performance on the mobile devices and network connections that are common among your users.

Loading Performance

This section lays out some recommendations for achieving the under 5 second loading time specified by RAIL above.

  • Always minify and bundle your Javascript and CSS.
    Also, the minified code should be separate from your development code (i.e., don't commit it to source control). You should use tools like handled by a framework like webpack to handle minification.
    • Caveat for bundling using HTTP/2:
      With HTTP/2, smaller bundles are preferable to larger ones. Consider using code splitting with frameworks like webpack, React or NextJs
  • Place javascript that is part of the critical rendering path in the head element of your page. Other scripts can be loaded asynchronously.
  • Always take advantage of tree shaking to prune code, especially when using external libraries.
  • Always compress resources server side, using GZIP or Brotli
  • Use caching via cache headers server side. You should use both the Expires and Cache-Control headers. Below are the values for Cache-Control:
    • no-cache: Somewhat of a misnomer, specifies that content can be cached but, if so, it must be re-validated on each request before being served to a client. This forces the client to check for freshness but allows it to avoid downloading the resource again if it has not changed. Mutually exclusive with no-store.
    • no-store: Indicates that the content actually cannot be cached in any way by any primary or intermediate cache. This is a good option for resources that may contain sensitive data, or for resources that will almost certainly change from visit to visit. Mutually exclusive with no-cache.
    • public: Indicates that the content can be cached by the browser and by any intermediate caches. Overrides the default private setting for requests that use HTTP authentication. Mutually exclusive with private.
    • private: Designates content that may be stored by the user's browser, but may not be cached by any intermediate caches. Often used for user-specific, but not particularly sensitive, data. Mutually exclusive with public.
    • max-age: Defines the maximum time that the content may be cached before it must be revalidated or downloaded again from the original server. This option generally replaces the expires header (see below) and takes a value in seconds, with a maximum valid age of one year (31536000 seconds).
    • Don't use an expiry greater than one year; that's effectively forever on the internet and, as noted above, is the maximum value for max-age under cache-control.
  • Reduce javascript and CSS code size. (Part of this process also involves evaluating whether you need to use frameworks like jQuery.)
  • Consider using HTTP/2
  • Consider using resource hints, such as rel="preload", especially for non-critical resources
  • Avoid loading resources more than once by using service workers
  • Always test site performance under poor connection speeds
  • Always lazy load offscreen assets such as images and video
  • Always consider your critical rendering path
  • Choose the right image format based on type of graphical content
    • Use PNGs for clip art, line drawings, or wherever you need transparency (lossless compression)
    • JPGs for photographs, and GIFs when you need animation. (Also consider video over animated gifs for animation.)
  • Optimize images using a tool like imagemin
  • Size images appropriately. For variable sizes based on device viewport size, look at using responsive images
  • If loading a number of smaller images on a page, consider bundling them into one image using sprites
  • Avoid overusing images. Prefer CSS over images for simple visual effects such as box shadow or rounded corners.
  • Consider using svg for high DPI screens (less load time)
  • Optimize svg images with tools like svgo

Web Performance Tools

  • Understanding lab vs. field data:
    • Lab data is performance data collected in a controlled environment with predefined devices and connections speeds
      • Strengths
        • Helpful for debugging performance issues
        • End-to-end and deep visibility into the UX
        • Reproducible testing and debugging environment
      • Limitations
        • Might not capture real-world bottlenecks
        • Cannot correlate against real-world page KPIs
    • Field data is performance data collected from real page loads your users are experiencing
  • Popular performance tools:

User-centric Performance Metrics

As mentioned previously, performance is all about user perception. Below is an in-depth summary of metrics related to this "perceived" performance:

  • First paint (FP) and first contentful paint (FCP):
    • FP: when a browser renders anything from the first page load
    • FCP: when the browser renders first bit of content from the DOM
  • First meaningful paint (FMP):
    • When the "first meaningful parts" are rendered.
    • Often this is content "above the fold"
    • If FMP is fast, it improves perceived speed, even if the rest of the page is still loading
  • Long Tasks:
    • Any task taking longer than 50ms to respond
    • Tasks run in the browser's main thread. So, long-running tasks can potentially block users
  • Time to Interactive (TTI)
    • When the website is both visually rendered and capable of reliably responding to user input
    • Things that can delay TTI:
      • The JavaScript needed to make the components on the page work hasn't yet loaded.
      • Another Long Task (described above)
  • To gather the metrics above in the real world (in addition to a testing tool), use the following Javascript APIs: PerformanceObserver, PerformanceEntry, and DOMHighResTimeStamp
  • And for more code samples related to real-world metrics, check out User-centric Performance Metrics on Web Fundamentals (the sections after "Measuring these metrics on real users' devices")
  • Finally, refer to the recommendations from Google Lighthouse on how to improve each of these metrics.

Rendering Performance

In addition to loading, rendering performance plays a major role in the perceived speed of a web page. While RAIL covered some key points related to rendering performance, Google lays out some finer points:

  • Screen refresh rate & user experience:
    • Most devices refresh their screens at 60 frames per second (FPS)
    • For scrolling, animations, and transitions to be smooth web pages need to make the browser match that rate of 60 FPS. Each frame must take no longer than 10ms to reach 60 FPS. The value of 10ms is calculated using this formula:
      framesPerSecond = 1s/60 = ~16ms; frameDurationLimit = 16ms - 6ms, where the 6ms is the time it takes for the browser to prepare the frame.
    • If 60 FPS is not met, users will begin to notice "jank" (aka choppiness)
  • Pixel Pipline:
    • A series of processes to render pixels to the screen
    • The pipeline consists of 5 major areas:
      • Javascript: often handles visual changes, such as toggling elements
      • Style calculations: figures out which elements to apply which css rules
      • Layout: determines the size and position of elements
      • Paint: drawing out text, colors, images, borders, and shadows, etc.
      • Composite: putting paint in correct order
    • Each of these processes above can push the browser below 60 FPS and introduce jank. Therefore, it's important to understand which ones your code is triggering:
      • Changing an element's layout (through css properties such as top or width triggers all pipeline processes. Layout changes are usually the most expensive.
      • Changing an element's "paint only" css property such as background image, text color, or shadows triggers all processes except Layout.
      • Changing a css property that requires neither paint nor layout, like opacity, triggers Composite. This change is the cheapest.
  • Recommendations for achieving a smooth, 60-FPS user experience:
    • Prefer using requestAnimationFrame over setTimeout or setInterval for visual updates.
    • For long-running javascript code, such as heavy computations, consider using Web Workers to free up the main browser thread
    • You can identify long-running javascript tasks using the performance tab in Chrome DevTools , under the "Main" section
    • Optimizing smaller javascript tasks won't have much of an effect on refresh rate
    • Reduce the complexity of CSS selectors. For example, prefer a simple class name over a complex selector like .box:nth-last-child(-n+1) .title. (Patterns like BEM can help with that.)
    • Reduce the amount of elements that need to be styled
    • Prefer Flexbox over older layout models
    • You can identify CSS recalculations using the performance tab in Chrome DevTools , under then "Main" section
    • Be aware that triggering Layout is the most expensive action
    • Avoid triggering Layout or Paint in the browser. Changing any property except transform or opacity will trigger repaints. (See "Pixel Pipeline" above).
    • In Javascript avoid excessive queries to layout-related CSS properties, since those queries will trigger Layout. For example, in the case of a loop, avoid reading with offsetWidth property of an element every time.
    • Use DOM layers to isolate expensive repaints. To force an element into a new DOM layer, use will-change: transform; for browsers that support will-change, or transform: translateZ(0);. Warning: use the later with caution and test heavily.
    • Debounce input handlers

Audit your site

  • Clean up your code as much as possible before running audits:
    • Remove unused code and assets
    • Address errors in the Chrome DevTools console
    • Lint your javascript, HTML and CSS
    • Remove Flash code (no duh, lol!)
    • Remove broken links and images
  • Review the testing tools and metrics defined previously in this post
  • Automate running google lighthouse on your pages.

Ok, that was a lot of material to chew on! Now let's continue to part three, where we finish out this series, summarizing the "Security" and "Base Technologies" sections.