Microlink cut WebGL screenshot time from ~24 seconds to ~6 seconds on GPU-less nodes by flipping a single Chrome flag — a 4× speedup that turned a frequent timeout into a routine operation.
Why Headless Servers Hate WebGL
Microlink's browser fleet runs commodity Linux nodes with no graphics card: no /dev/dri, no hardware acceleration. Cheaper and simpler, until you ask it to screenshot a three.js page. WebGL is a GPU API, so without hardware, Chrome has to emulate it entirely on the CPU. That emulation path was the bottleneck.
Chrome delegates WebGL to ANGLE, which translates calls to whatever the platform supports: Direct3D, Metal, OpenGL, or a software renderer. On a GPU-less Linux box, ANGLE picks either its bundled SwiftShader or the system OpenGL stack — which in practice is Mesa's llvmpipe. Same pixels, wildly different cost.
Why llvmpipe Runs Circles Around SwiftShader
SwiftShader emulates the pipeline conservatively. It aims for "draws correctly anywhere" and pays for it: a heavy 3D scene takes ~24s, simple 2D pages 2-3s. Llvmpipe takes a different approach. It JIT-compiles shaders and GL state into real x86-64 via LLVM — no interpreter loop. It's tiled and multi-threaded, using every core. Result: same output, ~4× faster.
The change is one line: replace --use-angle=swiftshader with --use-angle=gl. But you must not add back --disable-gpu (silently forces SwiftShader) or --in-process-gpu (kills the GL surface). And you need a display: --use-angle=gl binds an X surface. Without one, WebGL silently degrades to flat 2D — screenshot succeeds, status 200, output wrong but plausible.
The Hidden Work: Display, Build, and CI
Every container boots Xvfb before Chrome starts, with LIBGL_ALWAYS_SOFTWARE=1 pinning Mesa to llvmpipe. Ubuntu jammy's Mesa is too old; official PPAs are gone. So Microlink builds Mesa from source in a multi-stage Docker image: meson setup build -Dbuildtype=release -Dgallium-drivers=llvmpipe -Dvulkan-drivers= -Dllvm=enabled -Dshared-llvm=enabled. That gives shared LLVM (the JIT) and only llvmpipe. The build toolchain is huge (LLVM, clang, Rust, ~160 -dev packages), but the final image is 2.65GB vs 4.5GB by copying only artifacts.
Proving it works requires a live GL context query, not apt list. Microlink's browserless.report() reads gpu.type (must be software), gpu.device (must be llvmpipe), and simdWidth (256 means AVX2 active). A benchmark: true option runs a deterministic shader in ~300ms. CI asserts these values — any drift fails the build, catching the flat-2D fallback before it ships.
Numbers That Mattered
Same 3D chart, same GPU-less hardware, measured on production:
| Renderer | Isolated | Under Load |
|---|---|---|
| SwiftShader | ~24s | ~24s (timeouts) |
| Mesa llvmpipe | ~6s | 7–14s (all succeed) |
The requests that used to time out now finish. Microlink now treats 3D screenshots as a standard capability, not a special-case fallback.
Source: WebGL Without a GPU
Domain: microlink.io
Comments load interactively on the live page.