DK ✌🏽 Profile picture
Jul 16, 2021 33 tweets 15 min read Read on X
Today's #buildinpublic adventure:

Fixing the abysmal performance of @browserflowapp's landing page

Goal: Get Performance to green!
⚡️ Why this matters

Google is currently rolling out an update which incorporates performance metrics into its Search rankings. In other words, sites will be penalized if they're slow.

Also, I simply want Browserflow's site to be fast for everyone.

developers.google.com/search/blog/20…
🧐 Tools for investigation

I'm using web.dev/measure/ to measure my site's performance and understand it's so freaking slow.

Thankfully, it also provides suggestions for how to speed things up!
🔁 Testing process

browserflow.app is a Next.js app deployed on @vercel, which makes it easy to test out changes because every branch gets a preview build.

One thing to note is that since preview builds prevent indexing, the SEO score suffers but it's irrelevant here.
1️⃣ First metric: First Contentful Paint (FCP)

FCP marks the first point where the user can see anything on the page, which helps reassure the user that something is happening

Looks like this isn't a major offender so I'll revisit this after fixing more low-hanging fruit.
2️⃣ Largest Contentful Paint (LCP)

LCP marks the point when the largest image or text block in the viewport is rendered

My LCP is 2.0 seconds which beats the recommended goal of 2.5 seconds, but there's a catch...
My speed index is terrible. It takes nearly 13 seconds for the contents of the page to be populated!

Even though my FCP and LCP are good, the fact that it takes forever for the page to load means that it's still not a good user experience.
Why does it take so long for the page to load?

There's a bunch of unused Javascript that's either blocks rendering directly or competes for bandwidth, which slows down rendering.

I'm devastated to find that the biggest offender is one of my favorite tools: @loom 😭
It's also the biggest culprit for some of the other recommendations provided in the LCP tab like reducing the network payload.
I love using @loom to record product demos, so my lazy butt embedded it directly as my hero video.

To be fair, this is probably not at all how the Loom team intended this feature to be used.

This starts off our list of things to try:
💫 Experiment #1: Replace Loom embed
For now, let's go through the rest of the metrics.

3️⃣ Total Blocking Time (TBT)

TBT measures how non-interactive a page is prior to it becoming reliably interactive.

Oof, this one's bad, too.
Looks like I'm loading a bunch of Javascript, including some third-party code, that blocks the main thread and prevents the page from being responsive.
Let's work through the third-party code!

First up: Intercom (@intercom)

I can actually remove this since I'm not using it. If I add it back, I'll probably use github.com/devrnt/react-u… which allows delaying initializing for performance.

💫 Experiment #2: Remove Intercom
Next third-party code affecting performance: Stripe (@stripe)

Relevant issue: github.com/stripe/stripe-…

I load Stripe on every page for fraud detection, but I can worry about that when I have customers 😅

💫 Experiment 3: Limit Stripe to pricing page
Next third-party code affecting performance: Facebook (@Facebook)

...Wait, what? I don't use the Facebook SDK and I don't know why requests to FB are being made.

Oh, wow. Looks like the @loom embed makes the request. Okay, now I'm pretty excited to get rid of it.
Now let's see which Javascript uses the most CPU time.

It's our third-party friends, with @loom being the worst. Lesson learned: Don't embed Loom videos on pages where performance matters.

What's worse, they embed @userleap which is apparently some research tool. No thanks.
Last metric!

4️⃣ Cumulative Layout Shift (CLS)

CLS measures visual stability and helps quantify unexpected layout shifts. Like when you click on the wrong thing in Google Search because the "People also search for" box pops up out of nowhere. 😬

Yay, nothing to fix here!
👨🏻‍🔬 Time to run some experiments!

- Experiment #1: Replace Loom embed
- Experiment #2: Remove Intercom
- Experiment #3: Limit Stripe to pricing page
Experiment #1: Replace Loom embed

What happens if we get rid of the Loom embed entirely?

Performance score: 49🔴 → 62🟡
- Speed index: 12.8s → 1.7s (⬇️86.7%)
- Time to Interactive: 25.5s → 12.3s (⬇️51.8%)
- Total Blocking Time: 5,590ms → 5,190ms (⬇️7.2%)
This is a significant improvement, but not as big a jump in the overall score as I thought it'd be. (I was secretly hoping the score would jump to 90+ and I'd be done 😅)

Also, I still need to find a replacement since I still want the video.
Replaced the Loom embed with @wistia and...

My performance got absolutely destroyed.

Score: 62🟡 → 37🔴
Largest Contentful Paint: 1.1s → 7.8s (⬆️609%)

RIP
Why did my LCP take such a hit?

Or maybe a better question: How was it so good to start with? It was a mere 2.0s despite the Loom embed taking a while.

If we look at what the LCP element was, it wasn't the video taking up half the screen — it was the subtitle text...?!
Let's return to how LCP is defined. It's the point when the largest image or text block in the viewport is rendered. Stuff like <img> and <div> with text.

You know what's not included in that definition? <iframe>!!!

Guess what Loom uses to embed their videos?
The reason why my LCP took such a hit is because the element being used to measure LCP changed from a chunk of text (fast!) to an image fetched by Wistia's Javascript snippet (slow!).

Wistia also provides an iframe embed though. What would happen if we just...?
Score: 37🔴 → 55🟡
Largest Contentful Paint: 7.8s → 3.1s (⬇️60.2%)

Takeaway: Using an <iframe> embed instead of an image changes the LCP element which can significantly improve your LCP metric.
Now, this is obviously more of a hack than an actual solution.

The best thing to do is to ensure that the largest element actually seen by the user (the preview image for the video, regardless of what HTML element it's in) loads as quickly as possible.
Thankfully, Next.js makes it super easy for us to load that preview image quickly using `next/image`!

Experiment #1 results: 49🔴 → 62🟡

Our LCP slipped from green to yellow, but I think looking at the 3rd party JS will help with that as well. Time to move on to experiment #2!
💫 Experiment #2: Remove Intercom

Result: 62🟡 → 64🟡

Not much of a impact. TTI and TBT dropped, but the difference overall seems fairly negligible.

(Note: Given the variance between runs, I'm running the test until I get similar ones several times in a row.)
💫 Experiment #3: Limit Stripe to pricing page

Instead of importing Stripe on every page, I changed it to only be imported when it's needed.

Result: 64🟡 → 70🟡

That's a much bigger jump than I expected. Nice!
Final results 🥁

Performance score: 49🔴 → 70🟡
- Speed index: 12.8s → 4.3s (⬇️66.4%)
- Time to Interactive: 25.5s → 7.6s (⬇️70.2%)
- Total Blocking Time: 5,590ms → 850ms (⬇️84.8%)

Not bad for three minor changes!
Gonna stop here because (1) squeezing out extra performance is probably not worth it at this point and (2) I'm out of easy ideas 🙃

Takeaways:
- Love @loom but not a good idea to embed it on landing pages
- Remove unnecessary 3rd party scripts
- Next.js + @vercel is amazing ❤️
Surprise update!

@yefim nudged me to try hosting the files myself instead of embedding it from a 3rd party (Wistia) and I couldn't help but see what would happen...

The result: 💯 babyyyyyyyyy!
Final final results 🎉

Performance score: 49 🔴 → 95 🟢
- FCP: 1.7s → 1.0s (⬇️ 41.1%)
- Speed Index: 12.8s → 1.0s (⬇️ 92.1%)
- LCP: 2.0s → 1.2s (⬇️ 40.0%)
- Time to Interactive: 25.5s → 3.6s (⬇️ 85.9%)
- Total Blocking Time: 5,590ms → 240ms (⬇️ 95.7%)

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with DK ✌🏽

DK ✌🏽 Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @dk_the_human

Oct 19, 2020
In light of yet another popular Chrome extension becoming malware after a sale (Nano Defender), let's take a moment to appreciate @gorhill for tirelessly working on uBlock Origin since 2014 with utmost integrity and care. Thanks for being a champion of user privacy and security.
Here are some of the reasons why uBlock Origin is so great and why I tell friends to switch over when I see them using anything else 👇🏽
1. Principles over profit

Many content blockers lose their users' trust after becoming popular by selling to randos who steal user data or taking money from businesses to show their ads.

Even with 10M+ users, Raymond won't even take donations out of principle.
Read 6 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(