The core VENDOR and RENDERER parameters are deliberately bland: Chrome 154 1 returned WebKit 15,343 and WebKit WebGL on both renderers. WEBGL_debug_renderer_info adds UNMASKED_VENDOR_WEBGL and UNMASKED_RENDERER_WEBGL, the real strings a bug report needs, which Getting a Context's listing printed: Google Inc. (NVIDIA) and ANGLE 2,592 (NVIDIA, NVIDIA GeForce GTX 1650 (0x00001F82) Direct3D11 vs_5_0 ps_5_0, D3D11) here. Under SwiftShader 2,546 they read Google Inc. (Google) and the SwiftShader string of SwiftShader. The extension's own specification warns that these strings help fingerprint users and lets browsers mask them, so log them with error reports, never use them to decide whether WebGL works (test features, Checking Extensions). Its companion, WEBGL_debug_shaders, returns the source ANGLE actually compiled (ANGLE): HLSL here, SPIR-V under SwiftShader, which shows, for example, whether a loop was unrolled.
<!doctype html>
<style>
body { margin: 0; font: 11px system-ui, sans-serif; background: #f7f4ee; color: #333; }
.row { display: flex; gap: 10px; max-width: 600px; }
canvas { width: 200px; height: 200px; flex: none; }
table { border-collapse: collapse; font: 10px monospace; flex: 1; }
td { padding: 3px 4px; border-bottom: 1px solid #e3ddd0; vertical-align: top; word-break: break-all; }
pre { max-width: 600px; margin: 6px 0 0; font: 10px monospace; white-space: pre-wrap; max-height: 150px; overflow: auto; background: #fff; border: 1px solid #ddd; padding: 4px; }
</style>
<div class="row"><canvas id="c" width="200" height="200"></canvas><table id="card"></table></div>
<pre id="src"></pre>
<script>
const gl = document.getElementById('c').getContext('webgl2');
const fsSource = `#version 300 es
precision highp float; in vec2 vUV; out vec4 c;
void main() {
float rings = 0.0;
for (int i = 0; i < 4; i++) rings += 0.25 * step(0.5, fract(length(vUV - 0.5) * float(4 + i * 3))); // a small constant loop
c = vec4(mix(vec3(0.12, 0.37, 0.55), vec3(0.88, 0.6, 0.06), rings), 1.0);
}`;
const program = gl.createProgram();
const shaders = [[gl.VERTEX_SHADER, `#version 300 es
out vec2 vUV; void main() { vUV = vec2(gl_VertexID & 1, gl_VertexID >> 1) * 2.0; gl_Position = vec4(vUV * 2.0 - 1.0, 0.0, 1.0); }`],
[gl.FRAGMENT_SHADER, fsSource]].map(([type, src]) => {
const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); gl.attachShader(program, s); return s;
});
gl.linkProgram(program); gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());
gl.drawArrays(gl.TRIANGLES, 0, 3);
// The core strings are deliberately bland; the extension gives the real ones (they may be masked).
const info = gl.getExtension('WEBGL_debug_renderer_info');
const rows = [['VENDOR', gl.getParameter(gl.VENDOR)], ['RENDERER', gl.getParameter(gl.RENDERER)],
['UNMASKED_VENDOR_WEBGL', info ? gl.getParameter(info.UNMASKED_VENDOR_WEBGL) : '(extension not exposed)'],
['UNMASKED_RENDERER_WEBGL', info ? gl.getParameter(info.UNMASKED_RENDERER_WEBGL) : '(extension not exposed)'],
['VERSION', gl.getParameter(gl.VERSION)]];
document.getElementById('card').innerHTML = '<tr><td colspan="2"><b>for the bug report, never for feature detection</b></td></tr>' +
rows.map(([k, v]) => `<tr><td>${k}</td><td>${v}</td></tr>`).join('');
// What ANGLE actually compiled from the fragment shader: HLSL on Direct3D, SPIR-V or GLSL elsewhere.
const debugShaders = gl.getExtension('WEBGL_debug_shaders');
const translated = debugShaders ? debugShaders.getTranslatedShaderSource(shaders[1]) : '(WEBGL_debug_shaders not exposed)';
const lines = translated.split('\n');
const loopLines = lines.filter((l) => /for\s*\(|\[loop\]|\[unroll\]|while/.test(l));
document.getElementById('src').textContent =
`translated fragment shader: ${lines.length} lines; loop statements kept: ${loopLines.length}\n` +
(loopLines.length ? loopLines.slice(0, 4).join('\n') + '\n' : '') + '---\n' +
lines.filter((l) => l.trim()).slice(0, 40).join('\n');
</script>