Unity Mobile Game Development
Mobile browsers represent a massive audience. Over 60% of global web traffic comes from mobile devices, and many casual game players access browser games exclusively from their phones. A Unity WebGL game that works only on desktop misses the majority of potential players. The challenge is that mobile browsers impose stricter resource limits than desktop browsers, and the variety of devices means you cannot test on every possible configuration.
Step 1: Set Up Touch Input Handling
Unity's Input System package provides a unified API for both mouse and touch input, which simplifies cross-platform development. Touch input arrives as Pointer events, and a single codebase can handle both mouse clicks and finger taps through the same callbacks. Use the Pointer action type in your Input Action Asset, and the system automatically translates touch events into pointer positions and press/release states.
For gesture recognition, implement common mobile patterns: tap (short press and release), long press (hold beyond a threshold), swipe (press, move beyond a distance threshold in a direction, release), and pinch (two-touch distance change for zooming). Unity does not provide gesture detection out of the box, so you either implement it manually by tracking touch phases and positions, or use a third-party gesture library from the Asset Store.
A practical approach for a tap/swipe detector: record the touch start position and time on press, then on release calculate the distance moved and time elapsed. If the distance is under 20 pixels and time under 0.3 seconds, it is a tap. If the distance exceeds 50 pixels and time is under 0.5 seconds, it is a swipe, with direction determined by the angle of the movement vector. These thresholds should be tunable through serialized fields so you can adjust them during testing on real devices.
Virtual joysticks and on-screen buttons are essential for action games. The Unity UI system can render these controls as Canvas elements that respond to touch events. Keep virtual controls large enough for comfortable finger interaction (minimum 44x44 points, per Apple's Human Interface Guidelines) and position them in the lower corners of the screen where thumbs naturally rest. Make the controls semi-transparent so they do not obscure gameplay.
Step 2: Implement Responsive Canvas Sizing
Mobile devices come in a wide range of screen sizes, pixel densities, and aspect ratios. Your Unity WebGL canvas needs to adapt to all of them without letterboxing, stretching, or cutting off content.
In the WebGL template's HTML and JavaScript, listen for the resize and orientationchange window events. When either fires, update the canvas element's CSS dimensions to fill the available space and set the canvas rendering resolution to match. For high-DPI devices, multiply the CSS dimensions by a rendering scale factor. Using window.devicePixelRatio directly produces crisp visuals but can overwhelm mobile GPUs, so rendering at 0.5x to 0.75x the native pixel ratio and letting the browser upscale is a common compromise.
Inside Unity, set your Camera's orthographic size or field of view to accommodate different aspect ratios. For 2D games, use a reference resolution (such as 1920x1080) and set the Canvas Scaler to "Scale With Screen Size" with the "Match" slider adjusted so the game looks correct on both portrait and landscape orientations. For 3D games, adjust the camera's field of view or near/far planes dynamically based on the detected aspect ratio to prevent important content from being clipped on narrow screens.
Orientation locking is another consideration. If your game is designed for landscape play, the WebGL template can request landscape orientation through the Screen Orientation API: screen.orientation.lock('landscape'). This API requires a fullscreen context on most mobile browsers, so combine it with a "tap to play fullscreen" prompt on mobile devices.
Step 3: Optimize Memory for Mobile Browsers
Memory is the tightest constraint on mobile. iOS Safari enforces a per-page memory limit that varies by device but generally falls between 1 and 1.5 GB for newer iPhones. When your WebGL game exceeds this limit, Safari terminates the page with no warning or error, appearing to the player as a sudden crash.
Target a total memory footprint of 400-600 MB for comfortable iOS compatibility, leaving headroom for the browser itself and other background processes. Unity's WebGL Player Settings include an "Initial Memory Size" field that sets the starting WebAssembly heap. Set this to the minimum your game needs at startup (32 MB for a simple game, 64-128 MB for more complex titles) and let the heap grow as needed. Avoid setting it larger than necessary, because the full allocation is reserved at page load.
Texture memory is typically the largest consumer. Use compressed texture formats (ASTC on mobile, with ETC2 as a fallback) and limit maximum texture dimensions. A 2048x2048 RGBA texture uses 16 MB of GPU memory uncompressed. At 1024x1024 with ASTC compression, the same texture might use 1 MB. For UI elements, sprites, and distant background objects, 512x512 or smaller is almost always sufficient.
Load assets on demand rather than all at startup. Addressable Assets let you define groups that load when needed and unload when no longer visible. Structure your game so that scene transitions release the previous scene's assets before loading the next scene's assets, keeping peak memory usage as low as possible. Monitor memory in the browser with Chrome DevTools' Memory tab or Safari's Web Inspector to verify your actual footprint matches your expectations.
Step 4: Tune Rendering for Mobile GPUs
Mobile GPUs are significantly less powerful than desktop GPUs, and the thermal constraints of phones cause performance to degrade as the device heats up during sustained gameplay. Target 30 FPS as your baseline for mobile, with 60 FPS as a stretch goal for simpler games.
Use URP (Universal Render Pipeline) for WebGL builds, configured with the "Mobile" quality preset or a custom configuration. Limit real-time shadows to a single directional light with a low shadow resolution (512 or 1024). Disable post-processing effects that are computationally expensive on mobile: bloom, depth of field, motion blur, and ambient occlusion should either be disabled or use simplified mobile implementations.
Keep draw calls under 100-150 for mobile. Use static and dynamic batching, sprite atlasing for 2D games, and GPU instancing for repeated 3D objects. Reduce particle system counts and complexity: mobile GPUs struggle with large numbers of transparent, overlapping particles due to overdraw. Where possible, replace particle effects with simpler animated sprites.
Shader complexity matters more on mobile than desktop. Avoid per-pixel lighting with many lights, complex sampling patterns, and branching in fragment shaders. URP's "Simple Lit" shader is appropriate for most mobile WebGL content. For 2D games, the default sprite shaders perform well and rarely need modification.
Step 5: Handle Mobile Browser Behaviors
Mobile browsers have default touch behaviors that interfere with games. Scrolling, zooming, pull-to-refresh, and the browser's back gesture all trigger on touch events that your game needs to consume. In the WebGL template, call event.preventDefault() on touchstart, touchmove, and touchend events on the canvas element to prevent these defaults. Also add touch-action: none to the canvas CSS to suppress browser-level touch handling.
Audio autoplay restrictions on mobile require user interaction before audio can begin. The first tap, click, or touch event must trigger an AudioContext.resume() call to unlock audio playback. Unity handles this internally in most cases, but if you use custom audio through JavaScript, you need to manage the AudioContext state yourself. A common pattern is to show a "Tap to Start" screen that both initializes audio and enters fullscreen mode.
When the player switches tabs or locks their phone, the browser pauses or throttles your game. Handle the visibilitychange event to pause gameplay, stop timers, and mute audio when the page is hidden, then resume smoothly when it becomes visible again. Unity's OnApplicationPause and OnApplicationFocus callbacks fire in response to these browser events and provide C#-side hooks for pausing game logic.
Battery usage is a real concern for mobile players. Games that run the GPU and CPU at maximum utilization drain batteries quickly, leading players to close the game. Capping the frame rate to 30 FPS with Application.targetFrameRate = 30 reduces battery drain significantly compared to uncapped rendering, and most casual web games do not benefit visually from higher frame rates on mobile.
Step 6: Test on Real Devices
Emulators and responsive design mode in desktop browsers do not replicate the actual performance characteristics, memory limits, and touch behavior of real mobile devices. Test on physical devices representing your target audience.
At minimum, test on an iPhone (Safari is the only browser engine on iOS, so all iOS browsers behave identically), a mid-range Android phone (Chrome, which represents the majority of Android web traffic), and a lower-end Android device to establish your minimum supported hardware. Samsung Internet, Firefox for Android, and Opera are worth testing if your analytics show meaningful traffic from those browsers.
Use remote debugging to inspect performance on real devices. Chrome DevTools supports remote debugging for Android Chrome via USB. Safari Web Inspector supports remote debugging for iOS Safari when connected to a Mac. These tools let you profile CPU usage, GPU rendering, memory allocation, and network requests as the game runs on the actual device, providing data that desktop testing cannot replicate.
Mobile web game development is primarily about constraint management. Set conservative performance targets (30 FPS, 400 MB memory, under 150 draw calls), design your input for touch from the start rather than adapting mouse controls, and test on real devices early and often. The mobile audience is large enough to justify the optimization effort.