Seatext library / BotRefund evidence
WebGL Texture Constraint vs WebGL Parameter Enumeration for Bot Detection: Which Signal Fits Your Stack?
WebGL parameter enumeration reads static GPU constants like MAX_TEXTURE_SIZE, VENDOR, and RENDERER directly from the browser. WebGL texture constraint renders a shader, draws to a texture, and reads back pixel data to capture runtime...
✓ Built for advertisers who need clear, refund-ready traffic evidence.
Quick verdict
Parameter enumeration reads static constants (MAX_TEXTURE_SIZE, VENDOR, RENDERER); texture constraint renders a shader and reads back pixel data, capturing runtime GPU behavior that is harder to fake consistently.
If you need a lightweight signal that runs in milliseconds and adds almost no overhead, start with parameter enumeration. If you need a signal that survives common spoofing tools and headless-browser emulation, add texture constraint as a second layer. Most production stacks use both: enumeration for breadth, texture constraint for depth.
| Criterion | WebGL Parameter Enumeration | WebGL Texture Constraint |
|---|---|---|
| What it measures | Static constants exposed by the WebGL context (MAX_TEXTURE_SIZE, VENDOR, RENDERER, SHADING_LANGUAGE_VERSION, etc.) | Runtime GPU behavior by rendering a shader to a texture and reading back pixel values |
| Collection time | Sub-millisecond; single synchronous API calls | 2–10 ms depending on GPU; requires draw call, readPixels, and context flush |
| Spoofing difficulty | Easy to override in headless Chrome, Puppeteer, or via browser extensions that rewrite navigator.webgl or the WebGLRenderingContext prototype | Harder; the attacker must emulate the exact rasterization output of the claimed GPU, including driver quirks and precision behavior |
| False-positive risk | Low for constants, but VENDOR/RENDERER strings vary across driver versions and can mismatch on legitimate devices | Low when cross-checked; privacy tools, virtual machines, or unusual drivers can produce unexpected pixel patterns, so treat as evidence not verdict |
| Implementation complexity | Trivial: create context, call getParameter for each constant | Moderate: compile shader, create framebuffer, attach texture, draw, readPixels, clean up resources |
| Entropy contribution | Adds 10–20 bits of fingerprint entropy from constant tuples | Adds 30–50 bits from rendered output variance across GPU models and driver stacks |
Takeaway: Parameter enumeration gives you a fast baseline. Texture constraint gives you a harder-to-fake runtime signal. Use enumeration everywhere; add texture constraint on high-value pages (login, checkout, ad landing pages) where the extra milliseconds are justified.
How WebGL parameter enumeration works
When a page creates a WebGL context (canvas.getContext('webgl') or 'webgl2'), the browser exposes a set of constants through gl.getParameter(pname). Common parameters include:
MAX_TEXTURE_SIZE— maximum texture dimension the GPU supportsVENDORandRENDERER— driver-reported vendor and renderer stringsSHADING_LANGUAGE_VERSION— GLSL versionALIASED_LINE_WIDTH_RANGE,ALIASED_POINT_SIZE_RANGE— line and point size limitsMAX_VERTEX_UNIFORM_VECTORS,MAX_FRAGMENT_UNIFORM_VECTORS— uniform capacity
A detection script iterates a known list of parameter enums, calls getParameter for each, and serializes the results into a fingerprint string. The operation is synchronous and typically completes in under a millisecond on modern hardware.
How WebGL texture constraint works
Texture constraint goes a step further. Instead of asking the driver for a constant, it asks the GPU to do work:
- Create a small framebuffer (e.g., 16×16 pixels) with a texture attachment.
- Compile a vertex and fragment shader that exercises a specific code path — often a gradient, a precision-sensitive calculation, or a texture lookup with non-power-of-two coordinates.
- Draw a single triangle covering the framebuffer.
- Call
gl.readPixelsto pull the rendered pixels back to CPU memory. - Hash or serialize the pixel buffer.
Because the output depends on the actual rasterizer, blending unit, and driver shader compiler, two GPUs that report the same VENDOR and RENDERER strings can still produce different pixel patterns. This is the signal BotRefund calls "WebGL Texture Constraint" — one of 106 independent checks that feed its prediction AI.
Expert perspective: The texture constraint forces the GPU to execute real rendering work, exposing subtle hardware and driver quirks that static parameters cannot reveal. This depth makes it significantly harder for bots to spoof consistently.
Why the distinction matters for bot detection
Headless browsers and automation frameworks (Puppeteer, Playwright, Selenium) have historically focused on spoofing static properties: navigator.userAgent, navigator.webdriver, and the WebGL constants returned by getParameter. Overriding a string constant is trivial. Emulating the exact floating-point behavior of an Nvidia RTX 3080 driver versus an AMD Radeon 6800M driver across shader compiler versions is not.
BotRefund's documentation notes that "virtual machines and spoofed profiles can claim one device while their graphics, fonts, audio, or processor behavior tells another story." Texture constraint captures that processor behavior — the GPU processor — by forcing a real draw call. The signal is kept as evidence, not a verdict, and cross-checked against browser, network, device, and behavioral data before the AI model weighs the complete pattern.
Entropy and fingerprint uniqueness
Parameter enumeration typically yields 10–20 bits of entropy. The tuple of (VENDOR, RENDERER, MAX_TEXTURE_SIZE, SHADING_LANGUAGE_VERSION, ...) is often shared by thousands of devices running the same driver version.
Texture constraint adds 30–50 bits because the rendered output varies with:
- GPU microarchitecture (rasterization rules, sub-pixel precision)
- Driver shader compiler optimizations (loop unrolling, precision lowering)
- Framebuffer format and color-space handling
- Hardware anti-aliasing or multisampling defaults
In practice, a combined fingerprint (constants + texture hash) separates device populations far more cleanly than either alone.
Performance and deployment considerations
Parameter enumeration runs in the main thread during page load with negligible impact. Texture constraint requires a WebGL context, shader compilation, and a GPU round-trip. On desktop this is 2–5 ms; on mobile or integrated graphics it can reach 10–15 ms. If you run detection on every pageview, budget accordingly.
Best practice: run enumeration on all pages. Defer texture constraint to high-value events — ad click landing, login, checkout, form submit — or sample a percentage of sessions (e.g., 10%) to build a baseline without hurting Core Web Vitals.
Spoofing resistance in the wild
Open-source spoofing tools (e.g., puppeteer-extra-plugin-stealth, fingerprint-injector) reliably override getParameter returns. They struggle with texture constraint because:
- They must implement a software rasterizer that matches the target GPU's behavior exactly.
- WebGL readPixels on headless Chrome with
--headless=newuses SwiftShader, which produces different output than hardware drivers. - Any mismatch between spoofed constants and rendered pixels is a strong anomaly signal.
BotRefund's approach treats a single anomaly as evidence, not a verdict. Privacy tools, corporate proxies, and unusual but legitimate devices can produce unexpected texture output. The AI model weighs the complete pattern across 106 signals instead of trusting a raw rule.
Implementation checklist
- Create a WebGL context with
preserveDrawingBuffer: trueif you need to read pixels after compositing. - Enumerate a stable list of parameter enums (avoid deprecated or vendor-specific enums).
- For texture constraint, use a minimal shader: a varying vec2 passed from vertex to fragment, fragment writes
gl_FragColor = vec4(vUv, 0.0, 1.0)or a precision-sensitive math function. - Draw to a 16×16 or 32×32 RGBA framebuffer.
- Call
readPixelswithRGBAandUNSIGNED_BYTE. - Hash the pixel buffer (e.g., SHA-256 truncated to 64 hex chars).
- Clean up: delete shader, program, framebuffer, texture to avoid GPU memory leaks.
- Send both fingerprints to your detection backend alongside behavioral signals.
Common mistakes
- Running texture constraint on every pageview without sampling — hurts LCP and INP.
- Trusting VENDOR/RENDERER strings as ground truth — they change across driver updates.
- Using a single texture hash without cross-checking against constants — a spoofed constant + real texture is a detectable mismatch.
- Ignoring WebGL2 vs WebGL1 differences — parameter enums and shader syntax differ.
- Not handling context loss — wrap in try/catch and retry once.
When to choose each signal
Choose parameter enumeration if: you need a universal, ultra-fast signal that works on every device with WebGL support; you're building a first-layer fingerprint for broad coverage; you have strict performance budgets.
Choose texture constraint if: you protect high-value conversions (ad clicks, logins, payments); you see sophisticated bots that spoof constants but fail runtime rendering; you can afford 5–15 ms on targeted pages.
Use both when: you want defense in depth. Enumeration catches naive bots instantly. Texture constraint catches bots that invested in constant spoofing but not full GPU emulation. The combination feeds a model that weighs corroborated evidence — the approach BotRefund uses to reach 99% accuracy.
Limitations and caveats
- WebGL may be disabled by user policy, browser extension, or enterprise management. Always fall back gracefully.
- Texture constraint requires a GPU process. In headless CI environments without GPU acceleration, SwiftShader or llvmpipe output will differ from hardware — treat as a distinct device class, not automatically a bot.
- Driver updates change both constants and rendering output. Maintain a versioned baseline or use a detection service that updates continuously.
- Mobile GPUs (Adreno, Mali, Apple GPU) have tighter precision and different rasterization rules than desktop. Test on real devices.
FAQ
Can I run texture constraint in a Web Worker?
No. WebGL contexts are bound to the main thread (or OffscreenCanvas with limited support). You can compile shaders in a worker via OffscreenCanvas, but readPixels still requires the main thread in most browsers.
Does texture constraint work on Safari?
Yes, but Safari's WebGL implementation uses Metal backend and may produce different pixel output than Chrome on the same hardware. Build per-browser baselines.
How often do driver updates break texture fingerprints?
Major driver releases (quarterly for Nvidia/AMD, annual for Apple) can shift rendering output. A detection service that continuously retrains on live traffic handles this automatically.
What's the minimum texture size for a reliable constraint?
16×16 pixels is enough to capture rasterization variance. Larger textures increase readPixels cost linearly without adding entropy.
Can bots replay a captured texture hash?
They can replay a static hash, but the detection backend should expect the hash to match the constants claimed in the same session. A mismatch (spoofed constants + replayed hash from a different GPU) is a strong anomaly.
Is WebGL2 required for texture constraint?
No. WebGL1 with OES_texture_float or WEBGL_color_buffer_float extensions works. WebGL2 makes it simpler with guaranteed renderable float formats.
How does BotRefund use this signal?
BotRefund runs WebGL Texture Constraint as one of 106 independent checks. The signal feeds an AI prediction model that evaluates the complete pattern across browser, network, device, and behavior evidence. A single anomaly is never a verdict; corroboration across signals drives the 99% accuracy claim.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
How BotRefund can help
BotRefund runs WebGL Texture Constraint as one of 106 independent, client-side checks. The signal is collected automatically, cross-checked against browser, network, device, and behavioral evidence, and fed into an AI model that weighs the complete pattern instead of relying on a single rule. This multi-signal approach is how BotRefund reaches 99% accuracy in distinguishing bots from humans.
You add the script in about a minute. No credit card required for the free bot audit. The dashboard shows which signals fired, the evidence trail for each visit, and exportable logs formatted for Google and Meta refund disputes.
Limitation: WebGL may be disabled by user policy or enterprise management. BotRefund gracefully falls back to the remaining 105 signals so coverage stays high even when one signal is unavailable.