Compressing Game Assets for the Web

Updated June 2026
Compressing game assets is the highest-impact optimization for any web game because assets account for 80 to 95 percent of total download size. GPU-compressed textures via Basis Universal, geometry compression via Draco, audio encoding via Opus, and transport compression via Brotli together can reduce a 50 MB game to under 10 MB with no visible quality loss.

Every byte your player downloads delays the moment they start playing. Compression reduces those bytes, and the best compression strategies target the largest asset categories first. A typical web game's payload breaks down roughly as follows: textures consume 40 to 60 percent, audio takes 15 to 25 percent, meshes and animation data take 10 to 20 percent, and JavaScript code fills the remaining 5 to 15 percent. Compressing textures alone can cut total download size in half.

Inventory Your Assets by Type

Before compressing anything, categorize every asset in your project by type and measure its uncompressed size. List all texture files (PNG, JPEG, TGA, PSD source files), all mesh files (FBX, OBJ, glTF), all audio files (WAV, MP3, OGG), and all code bundles. Sum each category. The category with the largest total is your highest-priority compression target.

Many developers are surprised to find that a handful of large textures dominate their payload. A single 4096x4096 RGBA PNG can weigh 30 MB or more uncompressed. Ten such textures represent 300 MB of source art, which even with PNG compression might be 50 MB. Knowing these numbers before you start tells you exactly where to focus.

Compress Textures with GPU Formats

Traditional image formats like PNG and JPEG are designed for photos and illustrations. The browser must fully decompress them into raw RGBA bitmaps before uploading to the GPU, which means a 1 MB PNG becomes a 16 MB texture in GPU memory. GPU-compressed formats like ASTC, BC7, and ETC2 stay compressed in GPU memory, reducing VRAM usage by 4x to 8x.

Basis Universal is the standard tool for web game texture compression. It produces KTX2 container files that can be transcoded to whichever GPU format the player's hardware supports: ASTC on mobile, BC7 on desktop, ETC2 as a fallback. The transcoding happens in the browser at load time and takes only a few milliseconds per texture.

Basis Universal offers two encoding modes. UASTC preserves high quality and is best for textures where compression artifacts would be visible, such as character faces, UI elements, and normal maps. ETC1S produces smaller files at lower quality and works well for terrain, tileable patterns, and textures that will be viewed at a distance. A typical game uses UASTC for a handful of hero textures and ETC1S for everything else.

Run Basis Universal as part of your build pipeline so compressed textures are generated automatically whenever source art changes. The command-line tool basisu accepts PNG inputs and produces KTX2 outputs. Three.js, Babylon.js, PlayCanvas, and most other web engines include KTX2 loaders that handle transcoding transparently.

Compress 3D Meshes

The glTF 2.0 format is the standard for web 3D content, and it supports two major compression extensions: Draco and Meshopt. Both reduce geometry file size dramatically, but they work differently and have different strengths.

Draco is a geometry compression library from Google that encodes vertex positions, normals, UVs, and indices into a compact binary stream. It achieves 80 to 95 percent compression ratios by quantizing floating-point values and applying entropy coding. Draco is best when download size is the primary concern. The downside is that decompression requires a Wasm decoder on the client, which adds about 300 KB to your initial payload.

Meshopt takes a different approach. Instead of aggressive quantization, it reorders vertices and indices to maximize GPU cache utilization and then applies byte-level compression. The result is a file that is 50 to 70 percent smaller than uncompressed glTF and decompresses directly into GPU-ready buffers without the overhead of a separate decoder library. Meshopt is ideal when you want balanced compression with fast decompression and good rendering performance.

For most web games, Draco is the right choice for assets that load during level transitions where decompression time is acceptable. Meshopt is better for assets that stream during gameplay where decompression must be fast. You can use both in the same project for different asset categories.

Encode Audio with Opus

Opus is the best general-purpose audio codec for web games. It delivers better quality than MP3, AAC, and Vorbis at equivalent bitrates, and it is supported natively in every modern browser. For music tracks, encode at 96 to 128 kbps for high quality that most listeners cannot distinguish from the uncompressed source. For short sound effects like footsteps, impacts, and UI clicks, 48 to 64 kbps is sufficient.

Store your audio source files in WAV or FLAC format and encode to Opus as part of your build pipeline using opusenc or ffmpeg. Never transcode from one lossy format to another (for example, MP3 to Opus), as this compounds compression artifacts. Always start from a lossless source.

For music that plays continuously, consider streaming it with the HTML5 <audio> element or the Web Audio API rather than loading the entire file before playback. A three-minute music track at 128 kbps Opus is about 2.8 MB. Streaming it means the player hears music within a fraction of a second, while the rest downloads in the background.

Enable Transport Compression

Transport compression applies to the HTTP transfer layer and compresses files during delivery regardless of their internal format. Brotli is the modern standard, compressing 15 to 20 percent better than gzip for text-based content like JavaScript, JSON, HTML, and CSS.

Configure your web server or CDN to serve Brotli-compressed responses. Most CDNs support Brotli natively, including Cloudflare, AWS CloudFront, and Fastly. For self-hosted servers, nginx and Apache both have Brotli modules. Set the compression level to 6 for dynamic content (a good balance between speed and ratio) and pre-compress static files at level 11 for maximum compression.

Transport compression stacks with asset-specific compression. A Draco-compressed glTF file served with Brotli is smaller than either compression alone. A KTX2 texture gains less from Brotli because it is already a compact binary format, but JavaScript bundles and JSON data shrink by 70 to 80 percent with Brotli.

Minify and Tree-Shake Code

Use a modern JavaScript bundler like Rollup, Webpack, or esbuild to process your game code. Tree-shaking eliminates functions and modules that you import but never call, which can remove 20 to 40 percent of a large library. Minification renames variables to short identifiers, removes whitespace and comments, and simplifies expressions, reducing code size by another 30 to 50 percent.

Review your dependencies carefully. A utility library imported for a single function can add hundreds of kilobytes if the bundler cannot tree-shake it effectively. Prefer libraries that publish ES modules with proper sideEffects flags. When you only need one function from a large library, consider copying that function into your codebase instead.

Measure your final bundle size after minification and Brotli compression. A good target for the core game engine is under 200 KB compressed. If your bundle exceeds 500 KB compressed, look for opportunities to split it into smaller chunks that load on demand.

Validate and Measure Results

After applying all compression steps, reload your game with caching disabled and compare the new numbers against your original baseline. Verify that total transfer size decreased by your target amount. Check that visual quality is acceptable for every compressed texture, especially normal maps and UI elements where artifacts are most visible. Listen to compressed audio on headphones at full volume to catch artifacts that speakers might mask.

Test on a throttled connection simulating your target audience's network speed. The total size reduction should translate directly into faster load times. If load time improvement is less than expected, check for uncompressed files that slipped through your pipeline, or for server misconfiguration that is not serving Brotli responses.

Key Takeaway

Compress assets at every layer: GPU-compressed textures for VRAM savings, Draco or Meshopt for meshes, Opus for audio, and Brotli for transport. The combined effect routinely reduces total payload by 70 to 85 percent with no perceptible quality loss.