WebGL on Mobile Browsers: What Works

Updated June 2026
Mobile browsers account for over half of all web traffic, making mobile performance essential for any browser game that targets a wide audience. WebGL runs on virtually all modern mobile browsers, but mobile GPUs operate under severe constraints compared to desktop hardware. Success on mobile requires understanding these constraints and designing your rendering strategy around them from the beginning of the project, not as an afterthought.

The core challenge of mobile WebGL development is that mobile GPUs are designed for power efficiency, not raw performance. A flagship mobile GPU in 2026 delivers roughly 20-30% of the throughput of a mid-range desktop GPU, and mid-range phones (which represent the majority of devices worldwide) have significantly less capability than that. Every optimization technique for desktop WebGL is more important on mobile, and several mobile-specific concerns have no desktop equivalent.

Step 1: Understand Mobile GPU Constraints

Mobile GPUs use a tile-based rendering architecture (TBDR), which is fundamentally different from the immediate-mode rendering used by most desktop GPUs. In TBDR, the screen is divided into small tiles (typically 16x16 or 32x32 pixels), and each tile is rendered completely in fast on-chip memory before being written to the main framebuffer. This design reduces memory bandwidth consumption, which is the primary power bottleneck on mobile.

The practical implication for WebGL developers is that overdraw is expensive. Every pixel drawn more than once wastes fill rate and bandwidth. On desktop, overdraw is relatively cheap because memory bandwidth is abundant. On mobile, rendering the same pixel three or four times (from overlapping transparent objects, for example) can cut your frame rate in half. Sorting opaque objects front-to-back and minimizing transparent overlaps is critical.

Memory bandwidth limits how much texture data the GPU can read per frame. Large textures, uncompressed textures, and frequent texture switches all consume bandwidth. Mobile GPUs support compressed texture formats (ETC2 for Android, ASTC for both iOS and Android) that reduce texture memory by 4-8x compared to uncompressed RGBA. Using compressed textures is one of the highest-impact optimizations for mobile WebGL.

Shader complexity has a larger performance impact on mobile than desktop. Mobile GPUs have fewer arithmetic logic units and lower clock speeds. A fragment shader that runs fine on desktop may be too expensive per-pixel on mobile. The general rule is to avoid complex math in fragment shaders that execute for every pixel on screen. Move computations to the vertex shader (which runs per-vertex, a much smaller number) or precompute values into textures (lookup tables).

Step 2: Implement Resolution Scaling and Device Pixel Ratio

Modern mobile screens have very high pixel densities. A typical phone has a devicePixelRatio of 2 to 3, meaning the screen has 2 to 3 physical pixels for every CSS pixel. Rendering at full native resolution on a 1440x3200 phone means the GPU must shade over 4.6 million pixels per frame, which is more than a 1080p desktop monitor.

The most effective mobile optimization is rendering at a reduced resolution. Set the canvas width and height to the CSS dimensions multiplied by a fraction of devicePixelRatio. A factor of 0.5 to 0.75 provides a good balance between visual quality and performance. On high-DPI screens, the difference between full resolution and 75% resolution is barely perceptible, but the GPU workload drops by 44%.

Implement a resolution scale setting that adapts dynamically. Monitor the frame time (via the timestamp passed to requestAnimationFrame) and if frames consistently exceed 16.7ms (below 60fps), reduce the resolution scale. If frames are well under budget, increase it. This adaptive scaling keeps the game playable across a wide range of mobile hardware without requiring users to adjust settings manually.

Set the CSS size of the canvas with canvas.style.width and canvas.style.height to fill the desired area, and set the rendering resolution with canvas.width and canvas.height to the scaled pixel count. The browser upscales the lower-resolution rendering to fill the CSS area, which adds a slight blur but maintains consistent frame rates.

Step 3: Optimize Shaders for Mobile

Use mediump precision in fragment shaders for calculations that do not require full floating-point range. Most color calculations, texture coordinates, and lighting factors work fine at medium precision. Declare precision mediump float; at the top of the fragment shader, then selectively promote to highp only for calculations that need it (such as world-space positions for distance fog or shadow map sampling).

Reduce texture lookups per pixel. Each texture sample costs memory bandwidth and shader cycles. Where possible, pack multiple data channels into a single texture (for example, roughness in the alpha channel of the diffuse map, or ambient occlusion baked into vertex colors). Use texture atlases to combine multiple small textures into one large texture, reducing both texture switches and sampling overhead.

Avoid branching in fragment shaders. GPU cores execute in parallel groups (called warps or wavefronts), and if different pixels in the same group take different branches, all branches must execute for the entire group. Replace if-else blocks with mix(), step(), and smoothstep() where possible. For example, instead of branching on whether a pixel is in shadow, compute the shadow factor as a float and multiply.

Simplify lighting models for mobile. Replace Blinn-Phong with a simpler diffuse-only model for distant objects. Use baked lightmaps (light information pre-rendered into textures) instead of real-time per-pixel lighting. Limit the number of dynamic lights to 1-2 for mobile, and use light probes or spherical harmonics for ambient lighting instead of computing it per pixel.

Step 4: Handle Touch Input and Mobile Viewport

Mobile games require touch input handling that goes beyond translating mouse events. Use the touchstart, touchmove, touchend, and touchcancel events for responsive touch controls. Call event.preventDefault() on touch events to prevent the browser from scrolling, zooming, or triggering other default gestures that interfere with gameplay.

Set the viewport meta tag to prevent zooming and ensure consistent sizing: <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">. Without this, double-taps and pinches will trigger browser zoom, and the canvas may render at an unexpected size. Note that modern iOS Safari may still allow some zoom gestures even with this meta tag, so also handle the gesturestart event.

Implement virtual controls appropriate to your game genre. For movement, on-screen joysticks (rendered as canvas overlays or HTML elements) work well. For actions, large tap zones are more forgiving than small buttons. Place controls at the edges of the screen where thumbs naturally rest. Size touch targets to at least 48x48 CSS pixels (Apple's guideline) to avoid frustrating miss-taps.

Handle orientation changes by listening for the resize event and recalculating your canvas dimensions, viewport, and projection matrix. Some games lock to landscape mode using the Screen Orientation API (screen.orientation.lock('landscape')), which requires fullscreen mode. Provide a prompt asking the user to rotate their device if orientation lock is not available.

Step 5: Manage Thermal Throttling and Battery Life

Thermal throttling is the most insidious mobile performance problem. When the phone's internal temperature rises from sustained GPU activity, the operating system reduces the CPU and GPU clock speeds to prevent overheating. A game that starts at 60fps can drop to 30fps or lower after a few minutes of play, with no code change explaining the performance degradation.

Detect throttling by monitoring frame times. If the average frame time gradually increases over several seconds without any change in scene complexity, the device is likely throttling. Respond by reducing the rendering workload: lower the resolution scale, reduce particle counts, switch to simpler shaders, or disable post-processing effects. Many successful mobile games implement a "sustained performance mode" that targets 30fps instead of 60fps, keeping the GPU workload below the thermal threshold.

Reduce power consumption by avoiding unnecessary rendering. If the game is paused, in a menu, or showing static content, stop the render loop entirely or render at a reduced rate (10fps is sufficient for UI). Use requestAnimationFrame rather than setInterval for the render loop, since rAF automatically pauses when the tab is in the background, saving battery.

Minimize JavaScript garbage collection by pre-allocating objects and reusing them. On mobile, GC pauses are more noticeable because CPU speeds are lower. Avoid creating new vectors, matrices, or temporary objects inside the render loop. Use object pools for particles, projectiles, and other frequently created and destroyed game objects.

Key Takeaway

Mobile WebGL development succeeds or fails on three factors: rendering at a resolution the GPU can handle, keeping shaders simple enough for mobile hardware, and managing thermal throttling to maintain consistent performance over sustained play sessions. Design for mobile from the start rather than optimizing at the end.