For months, I was plagued by a performance issue on ColorBliss.
PageSpeed Insights reported 100+ seconds (!!!!) of total blocking time on my home page and a couple of my key landing pages (like this photo to coloring page converter).
I was starting to lose my organic rankings to competitors, and I was convinced that the poor performance on my marketing pages was part of it.
Having worked with a lot of companies on performance issues in the past, I knew that total blocking time (TBT) was one of the hardest Core Web Vitals to tackle. It usually involved getting deep into the weeds of the chrome dev tools performance tabs and untangling a mess of JavaScript code and dependencies to find slow downs.
Digging into PageSpeed Insights
Usually PageSpeed Insights gives you a helpful starting point, like Script Evaluation or Rendering. But not this time.
This time, 100+ seconds were showing up under "Other":
Ok, that's not helpful at all.
Digging Deeper with DevTools
So I did what I've always done in the past: I opened up the Chrome Dev Tools performance tab and started poking around at the on-page JavaScript. I removed packages, changed analytics implementations. But nothing worked.
Finally, I had enough, and assuming that it was an issue in my underlying Next.js usage, I decided to do an MVP of a migration to Astro for my marketing pages.
I got a new page all built out in Astro, and a lot of metrics were better! The first few tests were great.
But then I noticed that I was still seeing 10 to 30 seconds of total blocking time on some PageSpeed runs on a page WITH ABSOLUTELY no JavaScript.
It was infuriating, and I was stumped.
The Fix
At a complete loss at what would be causing it, I went to ChatGPT to see if it had any ideas at what would be going on:
Here's what it suggested:
- Render blocking fonts or CSS (look big or sync style tags)
- Audit 3rd party embeds or scripts (there were none)
- Images or assets decoding during paint (huge svgs or images can trigger main thread work during initial paint)
- Hydration edge cases
It gave me these specific tips:
- switch from .woff to .woff2 fonts and preload them explicitly.
- make sure css is split, purged, and async if possible.
- avoid huge base64-encoded SVGs inline in the html.
- remove ALL 3rd-party scripts temporarily, retest, then add back one by one.
Well, none of those in particular was going on, but the SVG call outs got me wondering. My hero had two SVGs included in it using tailwind, like this:
<div class="relative overflow-hidden bg-[url('/assets/hero-bg.svg')] bg-no-repeat bg-center bg-cover before:content-[''] before:absolute before:bg-[url('/assets/crosshatch.svg')] before:top-0 before:left-0 before:right-0 before:bg-top before:w-full before:h-full before:opacity-50 before:z-auto">
// rest of the code
</div>
Notice the two background svgs included in there?
One was a simple cross hatch illustration to add some texture, and the other had some Gaussian Blur in it to make this pretty color background:
I ran a test removing that background SVG with the blurred colors in it and to my surprise, Total Blocking Time dropped to 160ms.
I was shocked! I couldn't believe that something I thought was so simple caused such a huge issue rendering issue.
I removed the SVG, and swapped it out with a relatively simple css gradient:
.hero-gradient {
background:
radial-gradient(
circle at 36% 36%,
rgba(254, 164, 146, 0.4) 0%,
rgba(254, 164, 146, 0.2) 30%,
transparent 60%
),
radial-gradient(
circle at 66% 15%,
rgba(146, 196, 254, 0.7) 0%,
rgba(146, 196, 254, 0.3) 25%,
transparent 50%
),
linear-gradient(
135deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(240, 240, 240, 0.05) 100%
);
background-size:
100% 100%,
100% 100%,
100% 100%;
background-position: center center;
}
And it worked great. Achieved basically the same look, but with none of the performance issues.
But why was the SVG causing 100s of Total Blocking Time?
Well, that solved my problem, but I still didn't quite understand why. Here's what I leanred.
It turns out that the SVG blur function is incredibly expensive, and my blur radius was ver large.
Browsers are much more efficient at calculating CSS gradients - they draw it once with lightweight math.
To process the SVG blur, the browser has to take an offscreen image of my hero area, then for every pixel average a bunch of neighboring pixels, and do that over multiple passes. And I had standard deviation set to 150 (ie 150 smears). It came out to millions of pixel operations just to render that background.
Takeaways
Anyways, I learned that SVG usage can be an important place to look for performance issues. My leaning has always been that JavaScript is usually the culprit for most performance issues (and in my experience it usually is!) but if you are having a hard time tracking down your Total Blocking Time, it might be worth taking a look at some of your SVGs for expensive operations like blur.
Top comments (0)