gfx-tier · notes from production

Shipping WebGL into somebody else's iframe

Quality detection assumes it is running on a page you control. Ours is not. It runs embedded on other people's sites, in browsers that block, mask or simply lie about a third of the APIs we would like to ask.

Every 3D browser game has to answer one question before it draws a frame: how much can this device actually do? Get it wrong upward and a school Chromebook renders four frames a second. Get it wrong downward and a gaming desktop gets a blurry toy.

The usual answer is to probe: read the GPU string, count the cores, check the pixel ratio, pick a tier. That works fine on a page you own. It does not survive being embedded somewhere else, and every item below cost real debugging time to find.

What the browser will not tell you

What you wantWhat you get
WEBGL_debug_renderer_infoMissing entirely in privacy modes, or present and returning a masked, generic string. You cannot tell "unknown GPU" from "GPU that will not say".
devicePixelRatioHonest, and useless. A phone reports 3 while being unable to fill a third of those pixels at speed. Believing it is the fastest route to a slideshow.
hardwareConcurrencyClamped, absent, or reporting a number that has nothing to do with the GPU you are about to push.
deviceMemoryCoarse where it exists, absent where it does not, and missing on the platform you most need it.

So the entire probe is written defensively. Every single read goes through one wrapper:

function safe(fn, fallback) {
  try {
    const v = fn();
    return v === undefined || v === null ? fallback : v;
  } catch (e) {
    return fallback;
  }
}

Which looks like paranoia until you have watched a game die on someone else's page because an extension replaced one getter with a throw.

A wrong guess degrades quality. An exception kills the game. Those are not comparable failures, so nothing in this file is allowed to throw.

The one that cost a whole session

You need a WebGL context to ask about the GPU. The obvious move is to use the canvas you are about to render into — it already exists, and making a second one feels wasteful.

Do that and you have quietly ruined your renderer. The first getContext call on a canvas binds a context with the attributes you passed at that moment, and every later call returns that same context regardless of what you ask for. So the probe's throwaway attributes — no alpha, no depth, no antialias — become the settings the game renders with. Three.js then appears to accept your carefully chosen antialias: true and silently ignores it.

Nothing errors. The scene simply looks slightly wrong on every device, in a way that is very hard to attribute, because the code that chose the antialias flag is nowhere near the code that broke it.

The fix is one line of discipline: probe on a throwaway 1×1 canvas, read what you need, release it.

canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
gl = safe(() => canvas.getContext('webgl2', attrs), null);

Detection is a guess, so measure afterwards

Even with every probe working, the answer is an inference from proxies. A device that reports like a mid-tier laptop might be one with eleven other tabs open, a thermally throttled phone, or a machine running your game inside an iframe on a page that is itself doing heavy work.

So detection only picks the starting tier. A frame-rate guard corrects it from the one piece of evidence that is not a proxy — actual frame times — returning 'down', 'up' or null. The important part is the hysteresis: a single slow frame must never move the tier, or one hiccup makes the renderer thrash between settings, which looks far worse than simply sitting at the wrong tier.

Guess once, then measure. That pairing is what keeps a 3D browser game playable on a school Chromebook and still worth looking at on a desktop.

Take it

One file, zero dependencies, no build step, and it knows nothing about Three.js beyond the two lines that set renderer flags:

github.com/kentog751/gfx-tier · MIT

It has been running in production across several 3D titles at Free Games Online, on every device that has turned up — including the ones that lie.