Quick answer: Audit your build by asset category to find the biggest offenders (textures and audio, almost always). Apply platform-appropriate compression (ASTC for mobile, BC7 for PC), strip unused assets, convert audio to Vorbis/Opus, and set explicit size budgets per platform. Add a CI step that records build size after every commit and fails when the budget is exceeded. Track the trend weekly to catch gradual bloat.

Every megabyte of download size costs you players. On mobile, the drop-off above 200 MB is brutal — players on cellular connections simply skip the install. On Steam, a 40 GB download means your game sits in a queue behind whatever the player was already downloading. On console, large patches trigger “copying” operations that take longer than the download itself. Keeping your build small is a feature, and like any feature, it needs monitoring and a budget.

Auditing Your Build

Before you optimize, measure. Every engine can generate a build report that breaks down the output by asset category. In Unity, the Editor Log after a build contains a size breakdown by file type. In Godot, use --export-debug with verbose logging. In Unreal, the Size Map tool in the editor visualizes asset sizes as a treemap.

The breakdown almost always looks the same: textures are 40–60% of total size, audio is 15–30%, meshes are 5–15%, animations are 5–10%, and code plus everything else fills the remainder. If your numbers look different — for example, audio is 50% — that’s a strong signal that you have uncompressed audio files slipping into the build.

# build_size_report.py — parse Unity editor log
import re, sys, json

def parse_unity_log(log_path):
    categories = {}
    pattern = re.compile(
        r"^\s*([\d.]+)\s*(kb|mb|gb)\s+[\d.]+%\s+(.+)$",
        re.IGNORECASE
    )
    with open(log_path) as f:
        for line in f:
            m = pattern.match(line)
            if m:
                size = float(m.group(1))
                unit = m.group(2).lower()
                cat = m.group(3).strip()
                if unit == "gb": size *= 1024
                elif unit == "kb": size /= 1024
                categories[cat] = round(size, 2)

    return categories  # values in MB

report = parse_unity_log(sys.argv[1])
print(json.dumps(report, indent=2))

Run this after every build in CI and save the output as a build artifact. Over time, you build a historical record of how each category grows — and you can pinpoint exactly which commit added 300 MB of textures.

Texture Compression

Textures are the largest lever. Switching from uncompressed RGBA to a GPU-compressed format can reduce texture memory by 4–8x with minimal visual quality loss. The right format depends on the platform.

ASTC is the gold standard for mobile. It offers configurable block sizes (4x4 for highest quality, 8x8 for smallest size) and is supported on all modern iOS and Android devices. Use ASTC 6x6 as a default and drop to 4x4 only for textures where quality visibly suffers (UI elements, character faces).

BC7 is the standard for PC and console. It provides high quality at a fixed 8:1 compression ratio. Use BC7 for color textures and BC5 for normal maps. Avoid BC1 (DXT1) for anything with alpha — the 1-bit alpha channel produces ugly fringing.

Check your import settings for every texture in the project. In Unity, textures default to “Automatic” compression, which often picks a suboptimal format. Override per-platform in the texture importer. In Godot, set compress/mode to VRAM Compressed in the import dock. In Unreal, texture groups control compression settings per category.

Audio Format Choices

Audio is the silent size killer. A three-minute music track at 16-bit 44.1kHz WAV is about 30 MB. Compress it to Vorbis at 128 kbps and it drops to 3 MB. Multiply by a 40-track soundtrack and you have saved over a gigabyte.

Use Vorbis (OGG) for music and long ambient loops. Use ADPCM or Opus for short sound effects where decode latency matters. Never ship uncompressed WAV in a release build — it should only be in your source repository for editing. Your build pipeline should transcode to the compressed format automatically.

Check for duplicate audio files. It is surprisingly common to have the same sound effect imported multiple times under different names, or to have both a WAV source and an OGG compressed version in the project. A duplicate asset scan catches these instantly.

Stripping Unused Assets

Every game project accumulates dead assets: textures from a cut level, animations for a character that was redesigned, placeholder audio that was replaced. These assets still get included in the build if they are referenced anywhere in the dependency graph, even indirectly.

Run a reference scan to find unreferenced assets. In Unity, tools like “Asset Hunter” or the built-in “Find References in Scene” can identify orphaned files. In Godot, the --check-only export flag logs unused resources. In Unreal, the “Reference Viewer” shows the dependency graph for any asset.

“The asset you forgot to delete six months ago is still in your build, still inflating your download, and still costing you installs. Run a dead asset scan every sprint.”

Per-Platform Size Budgets

Set explicit size limits for each platform and treat them like performance budgets: exceeding them is a build failure, not a discussion point.

Reasonable starting budgets: mobile initial download under 200 MB (with asset bundles for additional content), PC under 5 GB for an indie title, console under 10 GB. These are not hard rules — a content-heavy RPG will naturally be larger than a puzzle game — but they should be intentional decisions, not accidents.

Break the total budget into category budgets: textures get 50%, audio gets 25%, everything else gets 25%. When a category exceeds its budget, the team that owns that category is responsible for finding savings before the build ships.

CI Size Tracking

Add a post-build step to your CI pipeline that records the total build size and compares it to the budget. If the build exceeds the budget, fail the pipeline with a clear message showing which category grew and by how much.

# ci_size_check.sh — post-build CI step
BUDGET_MB=500
BUILD_DIR="build/output"
SIZE_MB=$(du -sm "$BUILD_DIR" | cut -f1)

echo "Build size: ${SIZE_MB} MB (budget: ${BUDGET_MB} MB)"
echo "{ \"size_mb\": $SIZE_MB, \"budget_mb\": $BUDGET_MB }" \
  >> build_size_history.jsonl

if [ "$SIZE_MB" -gt "$BUDGET_MB" ]; then
  echo "ERROR: Build exceeds size budget by $(($SIZE_MB - $BUDGET_MB)) MB"
  exit 1
fi

Track the build_size_history.jsonl file over time. Plot it as a line chart in your team dashboard. The trend matters more than any single number — a build that grows by 5 MB every week will be 260 MB larger by end of year. Catching the trend early is the entire point of tracking.

Related Issues

If download size is impacting your game health scorecard, see How to Set Up a Game Health Scorecard. For pre-release checks that include size validation, check How to Build a Pre-Release Checklist Generator.

Every megabyte you save is a player who didn’t abandon the download. Treat size like a feature.