What the 38 Was Telling Me
The biggest offenders were unoptimised images (I was serving full-resolution PNGs where WebP thumbnails should have been), render-blocking scripts, and a 3D Three.js scene loading on the main thread without any code splitting. Each of those three issues alone would have been enough to fail a performance audit.
The Largest Contentful Paint was 5.8 seconds — almost entirely because the hero image was a 4MB JPEG. The browser was spending the first five seconds of the user's visit downloading a picture. Everything else was waiting behind that.
The Fixes That Moved the Score
Next.js Image component with proper width/height and priority on the LCP image. That alone cut the LCP by 3 seconds. Dynamic imports with React.lazy for the Three.js component — it now loads after the initial paint, not before. And moving all analytics scripts to load lazily with next/script strategy='lazyOnload'.
After those three changes: score went from 38 to 91. The three-second load became sub-second on a good connection. The lesson wasn't 'performance is hard' — it was 'I had been ignoring obvious things.'
Key takeaways
- Run Lighthouse before you share anything publicly — a score under 50 is a red flag that will hurt SEO rankings and first impressions
- The LCP image (usually your hero) is the single highest-leverage performance fix — serve it as WebP, give it a 'priority' prop in Next.js Image, and specify exact dimensions
- Lazy-load heavy third-party scripts (analytics, chat widgets) and large JS modules (Three.js, chart libraries) with dynamic imports to keep the initial bundle lean
Conclusion
Performance is a feature. A slow portfolio tells every visitor — including recruiters and potential clients — that you don't pay attention to fundamentals. The fixes are rarely hard once you know what Lighthouse is actually measuring.
Enjoyed this article?

Vivek Kumar Singh
Technical Expert · Full Stack Cloud Engineer · Tokyo, Japan