When Performance Breaks Before Launch.
We faced a common problem while developing a client site at our design agency recently. Everything looked great, but the site was really slow.
We tested it and found that the initial load time was more than 6 seconds. The Time To Interactive was more than 5 seconds, while the page scored 30/100 when we ran it through Lighthouse. Unfortunately, Lighthouse didn’t tell us what was actually causing it, so it was left to us to figure it out.
We finally found the cause of the problem after digging into DevTools performance traces. The site was loading Spline assets before the user needed them and they were blocking the main thread.
Lazy-loading those assets changed everything and this is what we got:
| Metric | Before | After |
| ----- | ----- | ----- |
| Initial Load Time | 6.5s | 1.2s |
| Time to Interactive | 5.3s | 0.8s |
| Largest Contentful Paint | 5.7s | 0.6s |
| Lighthouse Score | 30 → 90 | |
The load time went down to 1.2s, TTI down to 0.8s and the Lighthouse score shot up to 90. All these without changing the design or doing any compression hacks.
The load time went down to 1.2s, TTI down to 0.8s and the Lighthouse score shot up to 90. All these without changing the design or doing any compression hacks.
Context: The Build was Visually-Heavy
The project relied on multiple 3D spline embeds to create an immersive interface. Spline is great for this because it renders scalable 3D vector scenes. It’s also lighter than video or traditional 3D assets and allows live interaction. But it comes at the cost of having the runtime and model file load immediately unless you do something about it.
That means:
The Spline script executes before the page is interactive
The model downloads before the user scrolls to it
The browser treats it like a high-priority resource
Main thread execution stalls
Which is exactly what we saw in DevTools.
The Problem: Lighthouse Didn’t Flag the Bottleneck
Lighthouse gave us red flags about “render-blocking resources” and “heavy JavaScript execution”, but no mention of Spline directly.
It was difficult to spot because the asset didn’t show as an image or a large file. It came up as JavaScript execution time buried under other events. Only a DevTools Performance trace made it easier to see the problem:
Spline script was loaded in
<head>Execution started before anything else rendered
The main thread was blocked for hundreds of milliseconds
All meaningful content waited behind it
The page wasn’t slow because the assets were too large. It was slow because they loaded too early.
The Fix: Lazy-Loading the Spline Elements
We already lazy-load images, so we applied the same logic to Spline. Basically, don’t load it until the user is close to seeing it.
Spline Lazy Loading Pattern
function lazyLoadSpline(container) {`
`const observer = new IntersectionObserver(entries => {`
`entries.forEach(entry => {`
`if (entry.isIntersecting) {`
`entry.target.loadSplineAsset();`
`observer.unobserve(entry.target);`
`}`
`});`
`});`
`observer.observe(container);`
`}
Placeholder Strategy
We used static thumbnail placeholders to stop the layout from shifting. We also used a reserved container height and applied an optional loading state. All these stopped the layout from jumping when the embed initialized.
Why Spline Flies Under the Radar in Audits
Spline assets don’t behave like images, video or fonts, which means they don’t show up under “Large Media” or “Unused JS”. They also don’t trigger obvious Lighthouse warnings. Instead, they work like a hybrid of script execution, asset streaming and GPU rendering.
This means you can only catch the issue by looking at:
DevTools Network waterfall
Main thread flamegraph
CPU/JS execution timeline
Results: Before vs After
| Metric | Before | After |
| ----- | ----- | ----- |
| First Paint | 3.4s | 0.7s |
| LCP | 5.7s | 0.6s |
| TTI | 5.3s | 0.8s |
| Lighthouse | 30 | 90 |
In the end, we didn’t have to modify the Spline assets themselves or reduce resolution. We solved what seemed like a serious problem just by delaying when the Spline assets loaded.
Lessons Learned
| What We Learned | Why It Matters |
|---|---|
| Performance issues are often timing-based | Not just about file size |
| Lighthouse is not a full diagnostic tool | Always investigate manually |
| Lightweight assets can still block the main thread | Rendering work matters |
| Heavy visuals should never load before above-the-fold content | Prioritization > compression |
| Lazy loading is a performance technique, not just a UX feature | It frees CPU + bandwidth |
Practical Checklist for Future Builds
Based on the lessons learnt from the problem, we’ve created a checklist for future builds.
- Audit runtime-based assets, not just file sizes
- Treat 3D embeds like video: load only when needed
- Use IntersectionObserver for scroll-based activation
- Always test on mid-range real devices, not just desktop throttling
- Use placeholders to prevent layout shift (CLS)
- Record a DevTools performance trace before launch
- Don’t assume Lighthouse tells the whole story
Final Thoughts
Having smaller files isn’t the only thing to aim for if you want a faster website. You also have to make sure the right things load at the right time. Spline is a reliable tool for interactive web experiences, but you need to integrate it with performance in mind. All the site needed to become faster was moving from “load immediately” to “load when needed”. And we didn’t have to sacrifice visuals.
If Lighthouse isn’t showing you the real performance issue, it doesn’t mean there isn’t one. Sometimes you need to open DevTools and follow the execution timeline to find the fix.
Top comments (3)
Really insightful breakdown of how timing, not just asset size, can wreck performance—those before/after numbers are wild. Do you have any recommended resources, talks, or docs on profiling main-thread work for WebGL/3D embeds or on advanced use of DevTools performance tracing that helped shape this approach?
Appreciate this, the number change shook me too.
Honestly, a lot of this came from trial and error. I kept moving things and experimenting for a few days until I removed the spline assets and tested it.
Agba dev