Skip to article body
A woman in a dim Cairo workshop turning the wheel of an old cast iron machine, her arm smeared by motion
A woman in a dim Cairo workshop turning the wheel of an old cast iron machine, her arm smeared by motion

Web Development · Field Notes

The Test Machine Has No Graphics Card

Why our WebGL homepage scored 41 on PageSpeed Insights and ran fine on our own machines.

By Shehab Waleed, Founder, Cairo Studio 3 min read

Our homepage is a ring of projects drawn in WebGL. On our own machines it costs about 200ms of total blocking time. On PageSpeed Insights the same page cost 24 seconds and scored 41 on mobile.

We had already worked through the usual list. We trimmed the data the page inlines from 275KB to 82KB, loaded analytics later and lazy loaded images, and each of those was worth doing. None of them could explain 24 seconds.

Why a lab machine disagrees with your laptop

The test machines have no GPU. WebGL still works there, because Chrome falls back to SwiftShader, a software renderer. Two things then go wrong together. Shaders and draw calls all run on the CPU, and Chrome reads the canvas back on every frame, which shows up in a trace as GLES2::ReadPixels. A scene that barely registers on a GPU can block the main thread for the whole test.

Reproducing it locally

The flags are easy to get wrong.

lighthouse https://example.com \
--chrome-flags="--headless=new \
--use-angle=swiftshader \
--enable-unsafe-swiftshader \
--disable-gpu-compositing"

Plain --disable-gpu looks like the same test and is not. It turns WebGL off altogether, so the expensive path never runs and the problem stays hidden.

Detecting it

We ask WebGL which renderer it got.

let cached;

export function softwareGL() {
if (cached !== undefined) return cached;
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const info = gl?.getExtension('WEBGL_debug_renderer_info');
const name = gl
? String(gl.getParameter(
info ? info.UNMASKED_RENDERER_WEBGL : gl.RENDERER
))
: '';
gl?.getExtension('WEBGL_lose_context')?.loseContext();
cached = /swiftshader|llvmpipe|softpipe|software/i.test(name);
} catch {
cached = false;
}
return cached;
}

We check the renderer on purpose, and we do not look for a Lighthouse user agent. Serving test bots a different page is a way of lying to yourself. Anyone whose browser falls back to software rendering gets this path, which includes virtual machines and remote desktops.

What the lighter path changes

  • The pixel ratio drops to 0.5.
  • Antialiasing is off.
  • The entrance spin and post-processing are skipped.
  • The scene parks, and redraws only when something changes.

Shaders also compile with compileAsync, so they no longer block on the way in. The home page went from 41 to 80 on mobile, and scores 93 on desktop.

Measuring without fooling yourself

Lighthouse runs from a laptop can swing by 20 points on network jitter and local CPU load alone. Some habits that helped us:

  • Use the median of three runs.
  • Read environment.benchmarkIndex in the JSON report. On our machine, a value under about 3000 meant something else was hogging the CPU.
  • On PageSpeed Insights itself, a value under about 800 means you drew a slow machine. Run it again.

Our mobile home page still has a ceiling, because the hero stays hidden until its entrance animation runs, which delays the largest paint. That one is a design decision, and we have kept it. We wrote up other speed work we do separately.

Share
X LinkedIn Facebook WhatsApp Email
Hands turning a small brass crank, the motion blurred into a soft circle of light

The same work, done by hand.

24s

WebGL in software, canvas read back every frame

200ms

Same page, with a real GPU

41 to 80

After adding the lighter path

Cairo Studio homepage, measured October 2026.

The test machines have no GPU, so WebGL runs in software and Chrome reads the canvas back every frame. That can add many seconds of blocking time you will never see on your own computer.

Run Lighthouse with --use-angle=swiftshader, --enable-unsafe-swiftshader and --disable-gpu-compositing. Avoid plain --disable-gpu, which turns WebGL off and hides the problem.

Not if you do it by renderer rather than by spotting the test tool. Real people on virtual machines and remote desktops get the same lighter scene, and it runs better for them.

We build heavy interactive sites and test them under the conditions that lower your score.

More from the Journal

view all →