How We Achieved 98 PageSpeed Score: A Technical Deep Dive
When we launched ThreadCode's website, we didn't just want it to look good - we wanted it to be blazingly fast. After all, what kind of example would we set if our own site was slow? This is the story of how we optimized our website to achieve a 98 PageSpeed score, cutting load times by 78% and reducing total page weight by 92%.
This isn't theoretical advice - these are the exact techniques we used on the site you're reading right now. Every optimization is battle-tested and production-proven.
The Starting Point: Measuring the Problem
Before optimization, our metrics were embarrassing for a web development company:
- PageSpeed Score: 61/100 (Mobile)
- First Contentful Paint (FCP): 3.2s
- Largest Contentful Paint (LCP): 5.8s
- Total Page Weight: 4.2 MB
- Total Requests: 47 requests
The worst part? Our hero image alone was 2.1 MB. Unacceptable.
Optimization 1: WebP Images - The 97% Solution
This was our biggest win. Converting images from PNG/JPG to WebP reduced our total image weight from 3.8 MB to just 117 KB - a 97% reduction.
The Tool: npx sharp-cli
We used the fantastic sharp-cli tool to batch convert all images. Here's exactly what we ran:
# Install sharp-cli globally (or use npx)
npm install -g sharp-cli
# Convert all PNGs to WebP with 80% quality
npx sharp-cli -i "assets/images/*.png" -o "assets/images/{name}.webp" -f webp -q 80
# Convert all JPGs to WebP with 85% quality
npx sharp-cli -i "assets/images/*.jpg" -o "assets/images/{name}.webp" -f webp -q 85
# Resize and convert hero images
npx sharp-cli -i "assets/images/hero.png" -o "assets/images/hero.webp" \
-f webp -q 80 --width 1920 --height 1080 --fit cover
Before & After: Real Numbers
- ThreadCodeLogo.png: 856 KB → 24 KB (97% reduction)
- hero-bg.jpg: 2.1 MB → 68 KB (97% reduction)
- og-image.png: 632 KB → 18 KB (97% reduction)
- Total images: 3.8 MB → 117 KB (97% reduction)
Implementation: Progressive Enhancement
We used the <picture> element for progressive enhancement - serve WebP to browsers that support it, fall back to PNG/JPG for others:
<picture>
<source srcset="assets/images/ThreadCodeLogo.webp" type="image/webp">
<img src="assets/images/ThreadCodeLogo.png"
alt="ThreadCode Logo"
class="logo-image"
width="180"
height="60">
</picture>
Pro tip: Always specify width and height attributes to prevent layout shift (CLS).
npx sharp-cli on your images folder right now. It's the single highest-impact optimization you can make, taking less than 5 minutes for most projects.
Optimization 2: Removing Unused CSS & JavaScript
Our initial CSS file was 127 KB, but we were only using about 40% of it. Bootstrap, animations we didn't use, and legacy styles bloated the file.
What We Did
- Audited with Chrome DevTools: Coverage tab showed 63% unused CSS
- Removed Bootstrap: We weren't using most of it, wrote custom lightweight CSS instead
- Inlined critical CSS: Above-the-fold styles inlined in
<head> - Deferred non-critical CSS: Loaded remaining styles asynchronously
Critical CSS Inlining
<!-- Inline critical CSS for above-the-fold content -->
<style>
/* Navigation, hero section, fonts - 8KB inlined */
.navbar { /* ... */ }
.hero { /* ... */ }
/* ... */
</style>
<!-- Defer non-critical CSS -->
<link rel="preload" href="styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
Results
- CSS size: 127 KB → 38 KB (70% reduction)
- JavaScript size: 89 KB → 24 KB (73% reduction)
- Render-blocking resources: 4 → 0
Optimization 3: Lazy Loading Images & Scripts
Why load images and scripts for content users haven't scrolled to yet? Native lazy loading makes this trivial:
<!-- Native lazy loading (supported in all modern browsers) -->
<img src="portfolio-image.webp"
alt="Project Screenshot"
loading="lazy"
width="800"
height="600">
<!-- Lazy load iframes too -->
<iframe src="https://www.youtube.com/embed/video"
loading="lazy"></iframe>
For below-the-fold JavaScript, we used Intersection Observer:
// Lazy load analytics only when needed
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadAnalytics();
observer.disconnect();
}
});
});
observer.observe(document.querySelector('#analytics-trigger'));
Results
- Initial requests: 47 → 12 (74% reduction)
- Initial page weight: 4.2 MB → 324 KB (92% reduction)
Optimization 4: Font Optimization
Web fonts can block rendering. Here's how we optimized them:
Google Fonts Optimization
<!-- Preconnect to Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Load only needed weights with display=swap -->
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet">
The display=swap parameter shows text immediately with system fonts, then swaps when the web font loads. This eliminates FOIT (Flash of Invisible Text).
What We Avoided
We initially considered self-hosting fonts, but Google's CDN is faster and already cached for most users. The preconnect hints were enough.
Optimization 5: Minification & Compression
Gzip Compression
We enabled Gzip compression on our server (Vercel handles this automatically):
// vercel.json
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Encoding",
"value": "gzip"
}
]
}
]
}
HTML/CSS/JS Minification
We minified all production files:
# Minify CSS
npx clean-css-cli -o styles.min.css styles.css
# Minify JavaScript
npx terser script.js -o script.min.js -c -m
# Minify HTML
npx html-minifier --collapse-whitespace --remove-comments \
--minify-css true --minify-js true -o index.min.html index.html
Results
- HTML size: 42 KB → 28 KB (33% reduction)
- CSS size: 38 KB → 31 KB (18% reduction)
- JS size: 24 KB → 17 KB (29% reduction)
Optimization 6: Preloading Critical Resources
We used resource hints to tell the browser what to prioritize:
<!-- Preload critical font -->
<link rel="preload"
href="https://fonts.gstatic.com/s/plusjakartasans/..."
as="font"
type="font/woff2"
crossorigin>
<!-- Preload hero image -->
<link rel="preload"
href="assets/images/hero.webp"
as="image">
<!-- DNS prefetch for external resources -->
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
Optimization 7: Reducing Third-Party Scripts
Third-party scripts are performance killers. We audited every script:
What We Kept
- Google Analytics: Essential for metrics (loaded async)
- Lucide Icons: Lightweight icon library (10 KB)
What We Removed
- jQuery: Not needed, used vanilla JS instead
- Font Awesome: Replaced with Lucide (90% smaller)
- Social share widgets: Added simple native share buttons
// Native Web Share API (no third-party library needed)
async function shareArticle() {
if (navigator.share) {
await navigator.share({
title: document.title,
url: window.location.href
});
}
}
The Final Results
After implementing all optimizations, here's where we landed:
PageSpeed Metrics
- PageSpeed Score: 61 → 98 (+37 points)
- First Contentful Paint: 3.2s → 0.7s (78% faster)
- Largest Contentful Paint: 5.8s → 1.2s (79% faster)
- Time to Interactive: 6.1s → 1.4s (77% faster)
- Total Blocking Time: 890ms → 120ms (87% reduction)
- Cumulative Layout Shift: 0.18 → 0.02 (89% improvement)
Resource Metrics
- Total Page Weight: 4.2 MB → 324 KB (92% reduction)
- Total Requests: 47 → 12 (74% reduction)
- Image Weight: 3.8 MB → 117 KB (97% reduction)
Actionable Takeaways for Your Site
Here's your optimization checklist, ordered by impact:
- Convert images to WebP - Run
npx sharp-cli(5 minutes, 90%+ reduction) - Add lazy loading - Add
loading="lazy"to images (1 minute) - Enable compression - Gzip/Brotli on your server (5 minutes)
- Inline critical CSS - Above-the-fold styles in
<head>(30 minutes) - Remove unused CSS/JS - Use Chrome DevTools Coverage tab (1 hour)
- Optimize fonts - Add
display=swapand preconnect (5 minutes) - Audit third-party scripts - Remove what you don't absolutely need (1 hour)
- Minify everything - HTML/CSS/JS minification (10 minutes)
npx sharp-cli on your images folder and you'll see immediate results.
Tools We Used
- PageSpeed Insights: Primary testing tool
- Chrome DevTools: Coverage tab, Network tab, Lighthouse
- sharp-cli: Image conversion and optimization
- WebPageTest: Detailed performance waterfall
- GTmetrix: Alternative performance testing
Common Mistakes to Avoid
1. Over-Optimizing Too Early
We didn't optimize until we had content and traffic patterns. Premature optimization is wasted effort.
2. Breaking Functionality for Performance
We tested every optimization on real devices. Don't sacrifice user experience for a PageSpeed score.
3. Ignoring Mobile
70% of our traffic is mobile. We optimized for mobile first, desktop second.
4. Not Measuring the Impact
We tracked metrics before and after every change. Data beats assumptions.
Conclusion: Performance is a Feature
Achieving a 98 PageSpeed score wasn't about perfectionism - it was about providing the best possible user experience. Fast sites convert better, rank higher in Google, and respect users' time and bandwidth.
The optimizations in this article took us about 8 hours total to implement. The result: a 78% faster site that's now in the top 5% of all websites for performance.
Your site can achieve similar results. Start with images (the low-hanging fruit), then work through CSS, JavaScript, and third-party scripts. Measure everything, test on real devices, and don't stop until you hit 90+.
Performance isn't just about speed - it's about showing your users you care about their experience. Make it a priority.