Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Wed, 05 Mar 2025 21:31:25 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 225069128 Movies as Images https://frontendmasters.com/blog/movies-as-images/ https://frontendmasters.com/blog/movies-as-images/#comments Wed, 05 Mar 2025 20:14:19 +0000 https://frontendmasters.com/blog/?p=5281 Animated GIFs just suck for web performance, so don’t put them on websites. To quote Colin Bendell:

Animated GIFs are terrible for web performance. They are HUGE in size, impact cellular data bills, require more CPU and memory, cause repaints, and are battery killers. 

So if you need that same behavior of automatically playing silent moving images, we’ve learned to do to use a <video> instead:

<video autoplay loop muted inline 
  src="https://assets.codepen.io/3/fire-ring.mp4">
</video>

That’s… attribute soup. But it works. Plus there is a very good chance the .mp4 is way smaller in file size than the GIF would be.

My vote would be to always include controls as another attribute on there. Yes it adds some UI over the image, but that UI allows the user to hit a stop button, which is an accessibility feature and one I use often to stop forever-playing anyway. If you don’t, at least wire up some CSS or JavaScript that stops the playing on click.

Since 2017, Safari has supported this alternate approach:

<img src="https://assets.codepen.io/3/fire-ring.mp4" alt="" />

Just an <img> tag! No attributes to remember and get right, and it has the exact same behavior. Except the fact that there is no way to pause it, which is a bummer.

There are various ways to ship MP4-as-img by falling back to other techniques. When I started writing this and testing things out I was all prepared to try those and be annoyed at non-Safari browsers for not supporting this idea. But I’ve changed my tune. The fact that the <video>-based technique works fine across browsers and has a clear path toward pausing the movement makes me feel like MP4-as-img is just a sub-part technique and probably shouldn’t be used at all.

Examples:

]]>
https://frontendmasters.com/blog/movies-as-images/feed/ 1 5281
Optimizing Images for Web Performance https://frontendmasters.com/blog/optimizing-images-for-web-performance/ https://frontendmasters.com/blog/optimizing-images-for-web-performance/#comments Mon, 10 Feb 2025 14:33:55 +0000 https://frontendmasters.com/blog/?p=5153 Images make websites look great, but they’re also the biggest performance bottleneck. They add huge file sizes, delay Largest Contentful Paint (LCP), and can even mess with Cumulative Layout Shift (CLS) if they aren’t handled properly. And while developers are quick to optimize JavaScript and CSS, images are often ignored—despite being the heaviest assets on most pages.

So, how do we make images fast, responsive, and efficient? Here’s how:

Choose the Right Image Format

The format of an image has a massive impact on its size and loading speed. Picking the wrong one can easily double or triple your image payload. Check out this illustration: 

Size comparison of images in formats

To the user, it’s the exact same image, but the browser has to download 2x-10x more data depending on the format you pick.

Have a look at this photograph: 

Size comparison of photo in formats

Photographs are quite a bit more complex than illustrations (usually), and the best formats can change. Notice how JPG is smaller than PNG this time, but modern formats like WebP and AVIF are still way smaller.

  • JPG – Best for photos with lots of colors and gradients. Uses lossy compression to keep file sizes small.
  • PNG – Best for graphics, logos, and transparency. Uses lossless compression, but files can be huge.
  • WebP – A modern format that’s often smaller than JPG and PNG while keeping quality high.
  • AVIF – Even better compression than WebP, but not universally supported yet.

good rule of thumbJPG for photos, PNG for graphics, and use WebP or AVIF where possible for modern browsers.

Use Responsive Images

Not all users have the same screen size, so why serve the same massive image to everyone? Responsive images let you deliver the right image size based on the user’s device, reducing unnecessary downloads and improving load times.

Instead of a single <img> tag, try using a <picture> with the srcset and sizes attributes to tell the browser which image to load:

In this example, any screen less than 1400px wide will use an image from the srcset that is at least 100% of the viewport’s width. So if the screen is 1100px wide, the browser will select and download the hero-desktop-1024 version. This automatically scales images to match different devices, saving bandwidth and improving loading speed for smaller screens.

Lazy-Load Below the Fold

One of the worst offenders for slow performance? Loading every image on the page at once—even ones that aren’t visible. This is where lazy-loading comes in. Adding loading="lazy" to an <img> prevents it from downloading until it’s about to be seen.

<img 
  src="downpage-callout.jpg" 
  loading="lazy" 
  height="300"
  width="300"
>  

It’s very important to specify the height and width attributes of images, especially if they are going to be lazy-loaded. Setting these dimensions let’s the browser reserve space in your layout and prevent layout shifts when the content loads. For more about layout shifts and how to prevent them, check out this deep dive on Cumulative Layout Shift.

For images that are critical for rendering, like your LCP element, you should override lazy-loading with fetchpriority="high". This tells the browser to load it ASAP.

<img 
  src="downpage-callout.jpg" 
  fetchpriority="high" 
  height="300" 
  width="300"
>

Serve Images from a CDN

Content Delivery Network (CDN) stores images in multiple locations worldwide, so they load from the nearest server instead of your origin. This speeds up delivery and reduces bandwidth costs.

CDNs use modern HTTP Protocols

Most CDNs will also speed up your images by serving them with modern protocols like HTTP/3, which has significant performance improvements over both HTTP/1 and HTTP/2. Check out this case study on HTTP/3 performance.

HTTP Caching headers

Users always have to download an image at least once, but HTTP caching headers can help repeat visitors load them much faster. HTTP Caching headers instruct the browser to hold onto the image and use it again, rather than asking for it from the CDN again on the next visit. Here’s an example:

Cache-Control: public, max-age=31536000, immutable

This tells the browser that this image won’t change, and that it can be kept locally for 1 year without needing to be requested again. Caching isn’t just for images—it speeds up all static assets. If you’re not sure if your caching is set up correctly, there’s a full guide on HTTP caching that explains how to check and optimize it.

Wrapping Up

Images are one of the biggest opportunities for improving performance. By choosing the right format, compressing efficiently, lazy-loading, and leveraging CDNs with modern protocols, you can massively speed up your site.

If you’re looking for more image optimization tips, with detailed breakdown and real-world examples, check out the complete guide to optimizing website images.

]]>
https://frontendmasters.com/blog/optimizing-images-for-web-performance/feed/ 6 5153
Calibre Website Speed Test https://frontendmasters.com/blog/calibre-website-speed-test/ https://frontendmasters.com/blog/calibre-website-speed-test/#respond Tue, 17 Dec 2024 20:39:31 +0000 https://frontendmasters.com/blog/?p=4832 Calibre, the website performance testing app, launched a one-off Website Speed Test page anyone can use for free. This is nice it requires no special knowledge to use (anyone can type in a URL), the test can be run from significant geolocations (that probably aren’t where you live), and the test result is saved and can be shared.

webpagetest.org is the classic in this space. Both are trying to sell you more advanced services. Just a matter of which you find more useful.

]]>
https://frontendmasters.com/blog/calibre-website-speed-test/feed/ 0 4832
Mastering Interaction to Next Paint (INP) https://frontendmasters.com/blog/mastering-interaction-to-next-paint-inp/ https://frontendmasters.com/blog/mastering-interaction-to-next-paint-inp/#respond Tue, 05 Nov 2024 15:20:28 +0000 https://frontendmasters.com/blog/?p=4318 The web keeps changing—and so do the ways we measure web performance. Google recently updated its Core Web Vitals metrics, dropping First Input Delay (FID) in favor of a new and improved interaction metric: Interaction to Next Paint (INP).

FID was a step in the right direction, measuring how quickly a site responds to the first interaction. But it fell short by focusing only on that initial action, leaving out every click and type that follows. It also emphasized blocking time over total processing time, missing the real story behind user experience.

How is INP different? We’ll dive into exactly what it’s measuring, how to diagnose it, and, most importantly, how to fix it so your website interactions feel fast and responsive.

What “Interaction to Next Paint” Measures

INP monitors every interaction a user makes with a page—whether it’s a click, tap, or key press. It measures the time it takes for each of these interactions to trigger a visible change, or “next paint,” on the screen, capturing every millisecond from receiving the input to updating the display.

Over the lifetime of a page, there should (hopefully) be lots of user interactions. Your INP score is determined by the “worst” interaction on the page—the one with the longest delay until a response.

It may sound harsh, but it’s realistic. If just one interaction lags, that’s what users remember. By focusing on the most significant delays, INP spotlights the interactions that need the most attention to create a truly responsive experience.

To hit a good INP score, a page needs to respond to every interaction in under 200 milliseconds.

Why INP Matters

INP directly measures a website’s interactivity—how responsive the site feels to users as they interact. We’ve all had that frustrating experience of clicking a button, only to have nothing happen right away. Suddenly, you’re wondering if the click registered or if you need to try again. That’s the feeling of poor interactivity.

When interactions lag, users feel like the site is “slow,” “laggy,” or even “broken.” It’s a surefire way to frustrate users, lose engagement, and damage credibility.

And if that’s not enough, INP is one of Google’s Core Web Vitals and a factor in SEO rankings. Google doesn’t want to send search traffic to laggy sites, so it penalizes pages that fail their INP scores.

Understanding INP Measurement in Detail

Before we dive into tactics to fix INP, it’s important to understand how INP is measured. If you’re looking for even more details, check out this deep dive on Interaction to Next Paint.

Interactions

INP measures the response time for all discrete interactions—like clicks, taps, drags, and key presses—that users make while on the page. These actions initiate user commands and usually lead to some form of visual feedback, so tracking them is essential for accurately gauging a site’s interactivity.

One common action that doesn’t factor into INP, however, is scrolling. Scrolling is typically continuous rather than discrete and has its own performance considerations outside of INP’s scope.

The INP Measurement Window

INP measures everything that happens between a user interaction and the next paint event, and a lot can occur in that window. Here’s how it breaks down:

  1. Input Delay: When the interaction event is triggered, the browser may already be busy, for instance, executing an obnoxious amount of JavaScript. Since JavaScript can block the main thread, the browser has to wait until it’s finished before it can even start processing the interaction. This waiting period is the input delay.
  2. Event Handling: Once the main thread is free, the browser can dispatch the event to any associated event handlers—and there could be quite a few. Each event handler needs to be called and completed before moving forward.
  3. Rendering: After all JavaScript is done executing, the browser can calculate what changed in the DOM, update elements and layout, and finally paint the next frame to the viewport.

INP and Long Animation Frames (LoAF)

INP is closely related to Long Animation Frames (LoAF), as both metrics capture user responsiveness and visual feedback. LoAF measures the time between paint events, regardless of whether an interaction happened, indicating if the browser may have felt “frozen” to the user.

A slow INP is often accompanied by a Long Animation Frame, which can provide more details on the scripts or activities causing delays. For more information, check out this detailed guide on Long Animation Frames.

How to Improve Your INP Score

Improving your INP score means reducing the time users wait for visible feedback after they interact. Here are some strategies to make sure your site responds quickly:

  • Yield to the Main Thread: Avoid monopolizing the main thread with long-running tasks. When you block the main thread, user interactions get stuck waiting. Breaking up heavy tasks with setTimeout or requestAnimationFrame can reduce delays and keep the main thread open for user input.
  • Optimize JavaScript: Large JavaScript bundles are often the main culprit for interaction delays. Reduce bundle size through code-splitting, lazy-loading, and minifying to improve response times. Only load what’s necessary at each stage of the user journey.
  • Reduce Long Animation Frames (LoAF): A poor INP score often coincides with Long Animation Frames, so addressing LoAF can directly improve INP. This includes avoiding intensive calculations during animations and syncing visuals with the browser’s rendering cycle.

Implementing these strategies can bring down your INP score, keeping interactions responsive and making your site feel faster and more polished.

Example INP Fix

Let’s have a look at a specific example. Below is a CodePen with a simple form that includes a JavaScript event handler triggered each time the user types, changing the class on the button.

In this example, the JavaScript handler also calls a slow function, renderSearch, each time the event fires. Since renderSearch is part of the event handler, it slows down the entire animation frame and causes both LoAF and INP issues by blocking the main thread for too long.

Here’s how to fix it:

In our fix, we wrapped the event handler in requestAnimationFrame, which delays execution until just before the next paint. Then, we adjust the button class immediately, while the heavier work in renderSearch is delayed with setTimeout so it won’t execute until after the paint is complete. This approach prioritizes responsiveness and keeps user feedback snappy by yielding to the main thread.

Wrapping Up

Interaction to Next Paint (INP) gives us a more complete view of web interactivity by tracking every user interaction throughout a page’s lifecycle. With INP replacing FID as a Core Web Vital, optimizing for it has never been more critical. A strong INP score not only improves user satisfaction by keeping interactions fast and responsive but also boosts your SEO by aligning with Google’s performance standards.

By implementing strategies like yielding to the main thread, optimizing JavaScript, and addressing Long Animation Frames, you can create a smoother, more responsive experience for users. As you tackle these improvements, Real User Monitoring tools like Request Metrics can help you track INP and other Core Web Vitals in real-time, showing exactly where delays are occurring and how effective your fixes are.

Making your website interactive and smooth isn’t just about performance scores—it’s about ensuring users have a seamless experience on your site. With INP insights and a focus on responsiveness, you can keep users engaged, happy, and coming back for more.

]]>
https://frontendmasters.com/blog/mastering-interaction-to-next-paint-inp/feed/ 0 4318
Third Party Scripts https://frontendmasters.com/blog/third-party-scripts/ https://frontendmasters.com/blog/third-party-scripts/#respond Thu, 05 Sep 2024 21:18:08 +0000 https://frontendmasters.com/blog/?p=3763 Almost every site has at least one third-party script on it, and the average is 5. I’ve taken that from the blog post Introducing Nuxt Scripts from Harlan Wilton. While third-party scripts are a performance and security drain, they are, as Harlan puts it “fundamentally useful and aren’t going anywhere soon.”

Typically, you just chuck a third-party script in the HTML somewhere with a <script> tag that is high enough up that any usage of it comes after. But these days, with JavaScript frameworks abound, loading and using them is far more complicated. I applaud Nuxt for taking this head on and providing sensible helpers for them. Loading triggers and callbacks, types, bundling, etc.

Looks like it’s on the roadmap, but getting Partytown in there to gets the scripts off the main thread would be very good.

]]>
https://frontendmasters.com/blog/third-party-scripts/feed/ 0 3763
SVG triangle of compromise https://frontendmasters.com/blog/svg-triangle-of-compromise/ https://frontendmasters.com/blog/svg-triangle-of-compromise/#respond Tue, 30 Jul 2024 15:15:04 +0000 https://frontendmasters.com/blog/?p=3290 tag, it’s cached nicely, but you give up on CSS […]]]> I enjoyed Micah R Ledbetter’s SVG triangle of compromise and generally think it’s a fair analysis of how any-which-way you use SVG on a page, you’re giving up some kind of nice ability it could have. For instance, if you use SVG through an <img> tag, it’s cached nicely, but you give up on CSS reaching in there to style things. If you drop it in as <svg>, you can style, but then it’s not cached well for repeated uses.

Then Scott Jehl chimed in with a way to “have it all”. The crux of it is using the SVG <use> element to reference an SVG file (so you get caching and sizing) and you can set CSS --custom-properties that “pierce” the shadow DOM that <use> creates (that’s right, SVG can have a shadow DOM just like web components) and allow for styling.

This does solve all three angles, the caveats being 1) you can’t cache the SVG (“sprite”, it’s usually called when you combine icons into a single file) on a different domain. 2) it’s a manual pain to set up SVGs to be entirely styled in this way. Scott’s tool might help with 2, but browsers need to help with 1.

]]>
https://frontendmasters.com/blog/svg-triangle-of-compromise/feed/ 0 3290
Web Performance Guide https://frontendmasters.com/blog/web-performance-guide/ https://frontendmasters.com/blog/web-performance-guide/#respond Thu, 18 Jul 2024 22:20:02 +0000 https://frontendmasters.com/blog/?p=3061 I like how working on web performance is so well aligned with other worthy goals. A fast site is site more accessible to other people. A fast site tends to convert better. Using web standards and more native web technologies tends to lead to a faster site.

SpeedCurve has published a pretty beefy and useful guide to Web Performance, and Google’s guide is also good. Of course, our Web Performance Fundamentals course is best for a guided walkthrough of all the most important aspects.

]]>
https://frontendmasters.com/blog/web-performance-guide/feed/ 0 3061
The Pitfalls of In-App Browsers https://frontendmasters.com/blog/the-pitfalls-of-in-app-browsers/ https://frontendmasters.com/blog/the-pitfalls-of-in-app-browsers/#comments Wed, 17 Jul 2024 16:07:47 +0000 https://frontendmasters.com/blog/?p=3008 Developing websites for modern mobile devices has a pitfall you may not be aware of: in-app browsers. These are web browsers embedded directly within native mobile apps. So if a link is clicked within a native app (e.g. Instagram or TikTok), it uses the in-app browser instead of switching apps to a dedicated browser app.

While potentially convenient for mobile developers (i.e. users will never leave our app! the businessmen squeal), we’ll discuss the drawbacks for web developers like yourself and your users.

In-app browsers are also referred to as embedded browsers or WebView. These are interchangeable terms.

The Drawbacks

The drawbacks of in-app browsers can be broadly categorized:

Limited functionality

In-app browsers are considerably stripped down when compared to their fully-featured counterparts and typically lack features like bookmarking, UI controls, settings, extensions, and downloads. For for instance a browser extension that a user depends on or help protect their privacy will not work in an in-app browser.

Privacy & security concerns

Because in-app browsers are embedded within a native mobile app, the app developer has control and visibility into the users’ in-app browsing activity. This even extends into being able to inject code into the in-app browser which is a major privacy and security concern. Users are largely unaware and aren’t able to opt-out even if they are.

Inconsistent UI/UX

Because in-app browser implementations are all different, the UI is inconsistent. Further, browsing data like history and bookmarks aren’t shared so users typically need to sign into services they may already be securely signed into in their devices actual browser. This leads to a fragmented and frustrating user experience.

Worse performance

In-app browsers tend to be running outdated browser internals which can cause slower loading times and compatibility issues. Users on slower Internet connections may have the problem exacerbated.

Author Update: Since Apple doesn’t allow apps, even browsers, to use their own rendering engine only Android has the problem of bundling in a custom in-app browser instead of using the system WebView, which may be outdated and have worse performance. On iOS, the built-in WebView is bundled as part of the iOS WebKit. On Android, the default built-in WebView is based on the Blink version and is updated independently of the OS as part of the Chrome update process via Google Play.

Bad Behavior in In-App Browsers

History

In-app browsers have existed since circa 2016, but it wasn’t until 2019 when Thomas Steiner, a Google engineer, published a blog post that dove into Facebook’s iOS and Android apps that a wider audience was made aware of the privacy and security concerns. Thomas discussed the technical details behind how the apps implemented their in-app browsers and stated how in-app browsers can perform man-in-the-middle (MITM) attacks by injecting arbitrary JavaScript code and intercepting network traffic.

Three years later, in 2022, Felix Krause published two blog posts (1, 2) and a tool, inappbrowser.com, which focused on the privacy concerns of iOS apps. Initially this covered apps by Meta (Facebook, Messenger, Instagram) and then followed up with Android and other social media apps including TikTok. Felix’s findings supported Thomas’ from 3 years earlier and showed concerning findings from iOS Instagram: the arbitrary injection of a pcm.js script which Meta claimed to be an “event aggregator” but was also monitoring user interactions in the form of taps and selections. Further cause for concern was TikTok injecting JavaScript that monitors all keyboard inputs along with taps, which is effectively the functionality of a keylogger on third-party sites. TikTok acknowledged the existence of this code but claimed it’s only used for debugging, troubleshooting, and performance monitoring.

Felix’s findings led to a lawsuit being filed against Meta in September 2022. The case was dismissed in October 2023.

Nothing Has Changed

Let’s revisit the behavior of iOS & Android Instagram’s in-app browser at the time of this writing (July 2024). This is done by sharing the two testing links, inappbrowser.com and inappdebugger.com (we’ll discuss this one more shortly), in the app as a direct message or URL in your profile bio. This is so you can actually click on them, as Instagram prevents clickable URLs in places like the descriptions of posts.

Let’s start with iOS. Below is iOS Instagram opening inappbrowser.com and inappdebugger.com in July 2024:

This shows that iOS Instagram is still injecting arbitrary JavaScript code which listens to user clicks along with JavaScript messages.

(Editor note: when testing this I noted that Instagram also appends URL parameters on outgoing links, which may be used to communicate additional information to this injected JavaScript).

Next, Android.

The story on Android is slightly different: there’s still arbitrary JavaScript being injected but it isn’t necessarily listening to events tightly coupled with user interactions.

Unfortunately, not much has changed since Felix’s findings nearly 3 years ago.

Open Web Advocacy wrote a piece earlier this year following the events of Apple threatening to kill web apps.

Debugging & Detecting In-App Browsers

Leveraging the existing excellent work of Felix Krause and Shalanah Dawson we have strategies for debugging and detecting when our websites are being viewed by in-app browsers.

  • https://inappbrowser.com/
    • Attempts to detect if there’s any injected JavaScript code running.
  • https://inappdebugger.com/
    • Attempts to detect you’re in an in-app browser and, if so, which app it is inside of.
    • Additionally provides some debugging tests for if downloads are possible and escape-hatches for getting to an actual device browser.
    • Leverages both inapp-spy and bowser.
  • https://github.com/bowser-js/bowser
    • A browser detection library providing metadata and filtering based on browser version.
  • https://github.com/shalanah/inapp-spy
    • A TypeScript library written by Shalanah Dawson that aids in detecting in-app browsers.

Escaping

Now that we have some tools, let’s look at a example in JavaScript for detecting and redirecting in Android using an intent: link. You’d do this if you simply do not want your website being opened in an in-app browser, and offer a link to users to open it in their default browser instead.

import InAppSpy from "inapp-spy"
const { isInApp } = InAppSpy()

// Your app's full URL, maybe defined build-time for different environments
const url = `https://example.com`
const intentLink = `intent:${url}#Intent;end`

// 1. Detect in-app
if (isInApp) {

  // 2. Attempt to auto-redirect
  window.location.replace(intentLink)
    
  // 3. Append a native <a> with the same intent link
  const $div = document.createElement("div")
  $div.innerHTML = `
    <p>Tap the button to open in your default browser</p>
    <a href="${intentLink}" target="_blank">Open</a>
  `
  document.body.appendChild($div)
}

It’s not ideal to have to load extra JavaScript for this, but it is reliable. This may be heavy handed, but for those of you working on particularly sensitive sites, it might be worth doing.

To get an idea of a way this can be implemented, Shalanah’s inappdebugger.com provides this functionality under the “Android In-App Escape Links” section.

Test out the Android escape hatch strategy on inappdebugger.com.

Unfortunately, there’s currently no reliable way of handling iOS in-app browsers in terms of a proper escape hatch. Similar to Android, there’s a handful of device-specific URI schemes (that’s technically what the intent: prefix is called), but none of them are reliable to the default browser on a specific URL. A not-so-great workaround is using the x-web-search://? scheme, but the best-case is using the site: search prefix to get close to your actual URL e.g. x-web-search://?site:example.com.

Author Update: a somewhat reliable iOS workaround has been documented and tested by trying to run a Shortcut that doesn’t exist, specifying your URL in an error callback, and opening that in the user’s default browser. In practice, this looks like:

shortcuts://x-callback-url/run-shortcut?name=${crypto.randomUUID()}&x-error=${encodeURIComponent('https://example.com')}

This comes with some side effect caveats: the Shortcuts app is opened on the user’s device and some query parameters are appended to your URL. Read more on GitHub.

A last-ditch effort on iOS would be creating a UI element in your web app that gives the user manual instructions for bailing:

  1. Tapping the “…” menu
  2. Tapping on “Open in browser”
Screenshot of a UI that points to the upper right of the screen saying:

1. Click on the overflow menu (•••)
2. Then click Open in browser

This is considerably more fragile and error-prone, but if you have the metrics to where your user traffic is coming from and which in-app browser is preventing them from converting to your feature-rich PWA then it could be worth considering.

Hopefully, with time, we’ll see the fall of in-app browsers. The privacy and security concerns alone are unacceptable. Couple that with the limited functionality and poor user experience, it’s probably best they just went away. Thanks to groups like the Open Web Advocacy and individuals like Shalanah Dawson and Felix Krause for their work and support for this cause.

Recommended Reading

]]>
https://frontendmasters.com/blog/the-pitfalls-of-in-app-browsers/feed/ 3 3008
View transitions + speculative rules https://frontendmasters.com/blog/view-transitions-speculative-rules/ https://frontendmasters.com/blog/view-transitions-speculative-rules/#respond Thu, 11 Jul 2024 22:21:58 +0000 https://frontendmasters.com/blog/?p=3000 Ryan Seddon makes clear the potential performance problem with cross-page View Transitions:

… on a slow or spotty network, the transition may appear as if the screen is freezing, as the browser waits for the page to load before it can transition smoothly between the two screens—this is not ideal.

But also that our new friend the Speculation Rules API is a potential remedy:

Combining these two helps mitigate the original tradeoff of the “pause” between navigations while the browser loads the next document. With speculative prerender, it can render the page before the user clicks, making the transition near-instant.

Both these APIs are Chrome’n’friends only, so I guess it’s a you-break-it you-fix-it deal. They are also both progressive enhancements so no grave harm in using them now, unless you consider potentially unused pre-renders too wasteful.

]]>
https://frontendmasters.com/blog/view-transitions-speculative-rules/feed/ 0 3000
YouTube Embeds are Bananas Heavy and it’s Fixable https://frontendmasters.com/blog/youtube-embeds-are-bananas-heavy-and-its-fixable/ https://frontendmasters.com/blog/youtube-embeds-are-bananas-heavy-and-its-fixable/#comments Mon, 01 Jul 2024 12:56:07 +0000 https://frontendmasters.com/blog/?p=2881 TL;DR: YouTube Embeds are like 1.3MB in size with no shared resources between multiple embeds. Using a <lite-youtube> Web Component is more like 100k, does share resources, and sacrifices no functionality.

You can put a YouTube video on any website. They help you do it. Under the Share menu right on youtube.com there is an option to <> Embed and you’ll see bit of HTML with an <iframe> in it.

<iframe>s are never wonderful for performance, but they make sense for protected third-party content.

This is what I’m getting as I write:

<iframe 
  width="560" 
  height="315" 
  src="https://www.youtube.com/embed/LN1TQm942_U?si=EfW_M4bEHEO-idL3"
  title="YouTube video player"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
  referrerpolicy="strict-origin-when-cross-origin"
  allowfullscreen>
</iframe>

If I were Team YouTube, I’d get loading="lazy" on there to help with performance right away. No need for videos that aren’t even visible on the page to load right away.

<iframe 
  ...
  loading="lazy"
  >
</iframe>

Plus I’d put some inline styles on there to keep the video fluid and maintain the original aspect ratio. Or you could target these and do that yourself in CSS. Here’s assuming the videos are the standard 16 / 9 aspect ratio:

iframe[src^="https://www.youtube.com/embed/"] {
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 16 / 9;
}

But… let’s not keep this HTML at all. I’m sure you read this blog post title, but let’s put a point on it:

On a page with literally nothing at all on it other than a YouTube Embed, we’re looking at:

  • 32 requests
  • 1.3 MB of data transfer
  • 2.76s to load the page on my current WiFi connection

Zach Leatherman, equally exasperated by this, noted:

The weight also grows linearly with every embed—resources are not shared: two embeds weigh 2.4 MB; three embeds weigh 3.6 MB (you get the idea).

Wow.

Looks like sizes are up a bit since Zach last looked as well.

The Appearance & Functionality

This is what you get from a YouTube Embed:

  • You see a “poster” image of the video
  • You see the title of the video
  • You see a big play button — click it to play the video

This is very little UI and functionality, which is fine! We can absolutely do all this without this many resources.

Why is it this way? 🤷‍♀️

I don’t think we have any good answers here. In fact, I heard from a little birdie who ran it up the pole that they have tested lighter embeds and found them to reduce engagement. 😭

I’m just gonna straight up say I don’t believe it. It’s like when Google told us that taking up half the screen with AI generated answers led to people clicking on third-party results more, but then refused to show data or allow us to track those clicks ourselves.

And hey — sometimes there are unexpected results in testing. That’s why we test instead of guess. But because this is so counterintuitive and offtrack for so many other similar performance testing situations, this bears deeper scrutiny. It would benefit from an opening of the methodology and data.

Like if you tell me that if you hit people with a stick and they smile more, I’m gonna want you to stop until we can look at what’s going on there.

I really wish I could find a good link for this, but there is a famous story from YouTube engineers way-back-when who made a much lighter video page and put it into testing. They found, quite counterintuitively, that average page load times went up. But with a deeper look, they found that the lighter page was able to reach more people, including people on low-power low-internet-speed devices who were able to actually use YouTube for the first time, and them using it much more slowed those averages. That’s awesome! The speed of using the site was up relatively for everyone. The metric of the average page load speed was a red herring and ultimately not meaningful.

How do we know that’s not the same kind of thing happening here?

Remember the implications of all these resources isn’t just a little inconvenience. YouTube is so enormous we’re talking incredible amounts of wasted electricity and thus carbon output. Pulling a megabyte of data off every single YouTube Embed would be an incredible win all around. I might even say not improving this is environmentally negligent.

The Solution is to Replicate the Embed Experience Another Way. There are Open Source Web Components That Do It Well.

With a little dab of irony, Google’s own performance champion Paul Irish has had a web component doing just this for years and years and years:

lite-youtube-embed

The pitch is solid:

Provide videos with a supercharged focus on visual performance. This custom element renders just like the real thing but approximately 224× faster.

Two hundred and twenty four times faster. Which of course involves much less data transfer.

And I’d like to be very clear, also does the exact same thing as the default embed:

  • You see a “poster” image of the video
  • You see the title of the video
  • You see a big play button — click it to play the video

You lose nothing and gain tons of speed, efficiency, and default privacy.

Using Lite YouTube Embed

  1. Link up the JavaScript to instantiate the Web Component
  2. Use it

You could install it from npm or copy and paste a copy into your own project or whatever. Or link it from a CDN:

import "https://esm.sh/lite-youtube-embed";

That’s like this:

But the best way to use it is right in the README:

Use this as your HTML, load the script asynchronously, and let the JS progressively enhance it.

<script defer src="https://cdnjs.cloudflare.com/ajax/libs/lite-youtube-embed/0.3.2/lite-yt-embed.js"></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lite-youtube-embed/0.3.2/lite-yt-embed.css" integrity="sha512-utq8YFW0J2abvPCECXM0zfICnIVpbEpW4lI5gl01cdJu+Ct3W6GQMszVITXMtBLJunnaTp6bbzk5pheKX2XuXQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />

<lite-youtube videoid="ogfYd705cRs" style="background-image: url('https://i.ytimg.com/vi/ogfYd705cRs/hqdefault.jpg');">
  <a href="https://youtube.com/watch?v=ogfYd705cRs" class="lty-playbtn" title="Play Video">
    <span class="lyt-visually-hidden">Play Video: Keynote (Google I/O '18)</span>
  </a>
</lite-youtube>

With async loaded JavaScript, note the background-image is put into the HTML so it can all look right before the JavaScript loads.

Alternatives

]]>
https://frontendmasters.com/blog/youtube-embeds-are-bananas-heavy-and-its-fixable/feed/ 11 2881