A 3D website almost always looks fine on the machine it was built on. The interesting question is what it does on a four-year-old laptop with integrated graphics — and the only way to answer that is to instrument the page and read the numbers. We did that on our own gallery last week. Two of the three biggest problems were not what we expected, and one of them was a system we had added to help.
Why "it feels slow" is not a diagnosis
A 3D scene redraws the entire screen many times a second. When that gets expensive, the symptoms all feel identical to a visitor — stutter, lag, softness — but the causes are completely different and the fixes do not overlap. Guessing costs weeks.
So before changing anything, we wrapped the browser's own graphics calls to count what the page was really doing: how many images it handed to the graphics chip, how many times it rebuilt its internal canvases, and how often an automatic quality system changed its mind. All the figures below come from those runs on our own pages.
One honest caveat up front, because it governs how you should read everything here. Counts are exact. Frame rates are directional. A test machine's absolute speed moves by twenty per cent or more depending on what else is running, so we treat "91 network requests" as a fact and "68 frames per second" as a rough indication.
Finding one: the quality system was fighting itself
Like most serious 3D sites, ours has an adaptive quality system. It watches the frame rate and, if the device is struggling, quietly renders at a lower resolution so movement stays smooth. Five settings, sharpest to softest.
The problem is subtle enough that it survived months in production. Changing the setting is itself expensive. The page throws away its internal render surfaces and allocates new ones, which costs one slow frame. The quality system then measured the next second — which contained its own hiccup — concluded things had got worse, and dropped again.
On a throttled integrated laptop GPU we recorded the result:
- Four quality drops in under three seconds, straight to the lowest setting
- Then stuck there for the rest of the session, because the rule for climbing back needed a frame rate the lowest setting still could not reach
- 16 multisampled buffers allocated, 68 buffer deletions and 79 frames over 33 ms, all caused by the correction rather than the original problem
The second half of the same fault was worse, because it hit healthy devices. On a phone-sized viewport, during ordinary flick-scrolling:
- Three quality changes during a flick that averaged 59 frames per second. Fifty-nine. The device was completely fine
- 14 quality changes across one browsing session, with 56 buffer allocations and 172 deletions
Fast scrolling legitimately costs frames for a fraction of a second — new content arrives, textures upload, the scroll catches up. Reading that dip as evidence about the hardware, and acting on it, produced visible flickering in sharpness on a device that never needed help.
What fixed it
Three rules, none of them complicated, and the same three would apply to any adaptive system that reacts to its own output:
- A cooldown after every change. Do not measure the moment that contains your own disruption. We measured the actual cost of a change before choosing the length — a single frame of 25–158 ms, with the frame after it already normal — and set the cooldown from that rather than guessing
- Do not judge during interaction. Suspend measurement while the visitor is scrolling, plus a short tail so things settle. With a ceiling on how long that suspension can last, so a device that only struggles while moving still gets protection
- Require confirmation before dropping. Two consecutive bad readings, not one — with an exception for genuinely catastrophic frame rates while the page is idle, where there is nothing else to blame
After those three, quality changes during scrolling went to zero on both pages, and render-surface churn during scrolling went to zero with them.
Finding two: throwing textures away cost more than keeping them
Our gallery holds 51 pieces of artwork. To keep memory use low, the code released each texture once the visitor had walked past it. Walk back and it had to fetch the file, decode it and hand it to the graphics chip all over again.
Measured across one walk down the corridor and back plus six jumps to distant pieces:
- 91 network requests for 51 images
- 34 artworks handed to the graphics chip more than once
- 44 mipmap generations — the stack of progressively smaller copies a texture needs — repeated for images already processed once
Raising the retention budget so the full set stays resident changed those to 52 requests, zero repeat uploads and 22 mipmap generations. The cost was about 60 MB more graphics memory on desktop.
The genuinely useful part of that experiment was the middle of the range. We swept several budget sizes, and an intermediate setting performed worse than having no cache at all on the heaviest navigation pattern: large enough to evict something on every jump, too small to ever produce a hit, so it paid all the bookkeeping and delivered none of the benefit. If you add a cache, size it to hold the working set or do not add it.
We deliberately did not raise the same budget on phones. A phone that exhausts graphics memory loses its rendering context, and a blank screen is a far worse outcome than a slightly slower one.
Finding three: the instrument lied three times
We built a diagnostic panel into both pages so any device can report its own numbers. Reading it back immediately caught three faults — in the panel, not the site. This is the part most performance write-ups leave out.
- An average larger than its own 90th percentile. The panel reported a mean GPU time of 17.6 ms while claiming only ten per cent of frames exceeded 8.8 ms. Arithmetically impossible unless one enormous outlier is dragging the mean — and there was, a 537 ms start-up frame. Reporting the median instead fixed it, and changed the panel's verdict on what the page was limited by
- A frame that took 119 seconds. The tab had been in the background, where the page stops drawing entirely. The gap between the last frame before and the first frame after was recorded as a single frame
- "Smooth scrolling: off" on a page where it was demonstrably on. The panel was reading a variable that belonged to a different script
None of those would have been caught by looking at the page. A dashboard that is confidently wrong is more dangerous than no dashboard, because it sends you to fix things that are not broken.
What to take from this if you are commissioning a 3D site
Three questions worth asking whoever builds it:
- What does it do on a mid-range Android and a four-year-old laptop? Not "is it responsive" — what is the actual frame rate, and does quality change while scrolling
- Is there an automatic quality system, and can it recover? Many can only go down. One bad moment then degrades the site for the rest of the visit
- Can the site report its own numbers on a real device? If diagnosis requires the developer's machine, you will never be able to verify a complaint
Speed on a 3D site is not decoration. Google's Core Web Vitals are assessed at the 75th percentile of real visits, which means the slower quarter of your audience decides your score — and on a heavy page that quarter is exactly the group an adaptive system is supposed to protect.
You can see the result of all of this in our 3D gallery, and read more about why we build these at all in The Rise of 3D Web Experiences. If you are weighing up an immersive build for your own brand, our immersive 3D service covers what is involved, or you can tell us what you have in mind.
All measurements in this article were taken from Web Tactics' own production pages in August 2026, using wrapped WebGL entry points and animation-frame timestamps. Counts are exact; frame-rate figures are indicative, because a test machine's absolute speed varies with load.