Unity WebGL Build Optimization: Smaller, Faster, Better
PGP Studio Team · March 28, 2026 · 9 min read
How we cut a bloated WebGL build down to a fraction of its size, and made it load in seconds on 4G.

WebGL represents a highly valuable player acquisition surface: no install friction, one tap to play. But a page needs to start loading fast to retain that traffic; every extra second of load time bleeds players before they ever see the game. This is the specific build pipeline we run to take a bloated default WebGL export down to something that actually loads on a real connection.
Compression Format Is the First, Highest-Leverage Setting
Set Compression Format to Brotli in Player Settings under Publishing Settings. Brotli compresses noticeably tighter than gzip on the same asset set, though it's only natively decompressed by the browser itself over HTTPS in Chrome and Firefox. For everything else, Unity's Decompression Fallback option embeds a JavaScript decompressor directly in the build so the compressed asset still works without requiring special server-side MIME type or content-encoding configuration, which is the detail that trips up most teams deploying to a generic static host for the first time.
The tradeoff worth knowing about upfront: Brotli compression at build time is noticeably slower than gzip, which matters for CI pipeline duration but not for the shipped product itself, so there's rarely a good reason to stay on gzip once you've confirmed your hosting setup serves the right headers correctly. If a build is deploying to a CDN or static host you don't fully control, verify the actual served Content-Encoding header in the browser's network tab after deploying, not just in local testing, since misconfigured hosts silently serving uncompressed fallback content is a common and easy-to-miss regression.
Audio Is Usually the Single Largest Avoidable Payload
Convert high-fidelity background music into loop-enabled files at low bitrates, and stream audio dynamically at runtime instead of bundling it all into the initial download. Music and ambient audio are frequently the single largest asset category in a casual or mid-size game's build, and unlike textures or meshes, audio quality loss from moderate compression is far less perceptible to most players than equivalent quality loss in visual assets, making it one of the most favorable size-versus-quality tradeoffs available in the entire optimization pass.
For short sound effects specifically, keep them uncompressed or lightly compressed for latency reasons (a delayed or glitchy click sound is far more noticeable than a slightly lower-fidelity one), but for anything longer than a few seconds, particularly looping background music, aggressive compression paired with streaming rather than preloading is close to a free win, and it's usually the single change on this entire list that a team notices the least in terms of perceived quality loss relative to the file size it saves.
Strip Everything the Build Doesn't Actually Use
Strip unused engine components with custom build scripts rather than relying entirely on Unity's automatic stripping. Physics properties that are never active in a 2D-only layout, unused renderer features in the active render pipeline asset, and unreferenced package dependencies pulled in by default project templates can collectively shave several megabytes off a build with zero functional impact, but Unity's automatic managed code stripping doesn't catch every category of unused asset, particularly ScriptableObject-based configuration data that's technically referenced somewhere in the project but never actually used at runtime for a WebGL-specific build.
Auditing this properly means using Unity's own Build Report window after each release build, sorted by size, and treating any unexpectedly large category as a question to answer rather than an assumption to accept. It's common to find an entire unused render pipeline's shader variants, or a full physics engine's worth of code, silently included in a build that never touches either at runtime, simply because a default template setting was never revisited.
Texture and Mesh Compression Settings Need a WebGL-Specific Pass
Texture compression settings tuned for mobile platforms don't automatically carry over well to WebGL, since the target GPU and memory profile assumptions are different. Crunch compression specifically is worth enabling for WebGL textures where it isn't already, since it trades a small amount of build-time compression cost for a meaningfully smaller download size, and the runtime decompression cost is negligible on the desktop and modern mobile browsers that make up the overwhelming majority of WebGL traffic.
Mesh compression follows a similar pattern: aggressive vertex compression settings that would be a bad tradeoff for a photorealistic AAA mobile title are usually a clear net win for the lower-poly, stylized art style that dominates casual and hyper-casual WebGL titles, where the visual difference is imperceptible but the download size difference is not.
Loading Screen Design Matters as Much as Actual Load Time
Perceived load time and actual load time are not the same problem, and a well-designed loading screen with real, incrementing progress feedback measurably reduces abandonment even when total load time is unchanged. A generic spinner with no progress indication reads as broken or stalled to an impatient player far sooner than an honest progress bar tied to Unity's actual `Application.LoadProgress` value, even at an identical real load duration.
Beyond the progress bar itself, consider splitting the load into a fast initial payload (enough to render the title screen and start the game visually) with remaining assets streamed in during a short 'press to continue' moment, rather than blocking the entire experience behind a single monolithic load. This pattern, borrowed from web performance practice more broadly, applies directly to WebGL games and is underused in the Unity WebGL ecosystem specifically.
CDN and Hosting Configuration Is Part of the Optimization, Not an Afterthought
All the build-side compression work described above is undermined if the hosting layer serving the build isn't configured correctly, and this is where we've seen otherwise well-optimized builds quietly underperform in production despite passing every local test. Serving build assets from a proper CDN with edge caching close to the player's actual location matters more for WebGL than for a typical web app, since the initial payload is large relative to most web content and every additional hundred milliseconds of round-trip latency to a distant origin server compounds across the several separate asset requests a WebGL build typically makes.
Cache headers deserve specific attention: build output files include a content hash in their filename by default, which means they're safe to cache aggressively and indefinitely at the CDN and browser level, since a new build produces new filenames rather than overwriting old ones. Many default static hosting configurations don't set sufficiently long cache lifetimes for these hashed assets out of general caution, which costs repeat visitors a full re-download of unchanged assets on every visit for no real benefit, when a simple far-future cache-control header on the hashed build folder would let the browser skip that redundant transfer entirely on every return visit.
Measure on the Actual Connection Speed Your Players Have
It's easy to test WebGL load performance exclusively on a fast office or home connection and never notice a real problem, since a build that loads in two seconds on fiber can take ten or more seconds on a throttled or congested mobile connection, which is exactly the segment of WebGL traffic (shared links, ads, social embeds) most likely to abandon a slow load before it finishes. Chrome DevTools' network throttling presets, set to a realistic 'Slow 3G' or 'Fast 3G' profile, should be a standard part of every WebGL release check, not an occasional spot check, and the same throttled test should be repeated on an actual mobile device over a real cellular connection before any release, since desktop throttling simulations don't perfectly reproduce mobile network behavior.
Structure the Build Around Progressive Asset Loading
Beyond compressing what you already load, restructuring what loads when is often the bigger architectural win. Unity's Addressables system, applied deliberately to a WebGL target, lets you split a build into a small core bundle needed for the title screen and first playable moment, with additional content bundles (later levels, cosmetic assets, rarely-used UI states) fetched on demand rather than bundled into the initial payload every player downloads regardless of how far they actually get into the game.
This matters disproportionately for WebGL specifically because of where WebGL traffic actually comes from: ad network playable previews, social media embeds, and cold links from search, all of which see a large share of visitors who never progress past the first minute of play. Front-loading an entire game's worth of assets for an audience where a meaningful fraction never gets past level one is a direct, avoidable tax on load time for every single visitor, paid regardless of how far they end up playing.
Implementing this well requires discipline about what counts as 'core': the actual first-session critical path (title screen, tutorial or first level, essential UI chrome) should be the only thing in the initial bundle, with everything else, including audio beyond the first level's music, streamed in afterward. Retrofitting Addressables onto a project not built with this split in mind from the start is real work, but for any WebGL title expecting meaningful ad-driven or social traffic, it's worth treating as a first-class architecture decision rather than a late optimization pass.
Browser Memory Limits Are a Real, Underestimated Constraint
WebGL builds run inside the browser's own memory sandbox, and that sandbox is meaningfully more constrained than a native mobile app's available memory, particularly on mobile browsers where the constraint is tighter still and less forgiving of a build that assumes desktop-level headroom. A build that never crashes in desktop Chrome during development can hit an out-of-memory tab crash on mobile Safari or Chrome for Android that a team without dedicated mobile-browser testing simply never encounters until real players report it.
Unity's memory profiler, run against an actual exported WebGL build rather than the Editor (since Editor memory behavior doesn't map cleanly to a WebGL build's heap constraints) and tested specifically on a real mobile browser rather than a desktop simulation, is the only reliable way to catch this class of problem before it reaches players. Texture memory in particular deserves close attention, since WebGL builds don't benefit from the same texture streaming and platform-level memory management tricks available on native mobile, making explicit, conservative texture budgets more important on this platform than almost any other Unity export target.
The full pipeline described above (Brotli compression with proper server configuration, streamed rather than preloaded audio, aggressive and audited asset stripping, WebGL-specific texture and mesh compression, progressive asset loading via Addressables, honest loading feedback, and mobile-browser-specific memory testing) is what took our own experimental WebGL builds from an unusable multi-tens-of-seconds load on a throttled connection down to a load time that keeps the vast majority of clicked-through traffic actually watching the loading bar instead of closing the tab. Every optimization covered here gets re-verified on a real, representative connection speed before we call a WebGL build finished, since a build that loads quickly on a development machine's fast, wired connection can still disappoint on the mobile connections a large share of browser-based players actually use.
Our Unity Mobile Performance Optimization guide walks through this same discipline applied to the mobile side of a cross-platform build, since a WebGL target rarely ships alone. Build size and frame-time budgets like these are standard on every Unity development project we scope, not an optional extra.
Need Unity development help?
From prototype to store-ready release, this is exactly what we do for client engagements.
Explore Unity Development