Originally published on Hashnode
On this page
One of the long-standing complaints about Godot for Web/H5 games is export size. For H5 games, package size matters a lot: platforms often impose size limits, and a larger initial download means longer loading times, which directly affects player retention.
Compared with Unity, Godot is less convenient in this area. Godot Web export does not support C#, and its starting package size is also larger.
I ran a few experiments to see how far a Godot 4.7 Web build can be compressed, and which size optimization methods are actually useful.
Here is the result upfront:
-
Official default Web template, empty project, raw size:
46.24 MiB -
Extreme trimming + post-processing + brotli transfer compression:
3.00 MiB
Raw Export Without Any Optimization
First, I exported the empty project directly with the official Web export template.
| Case | Raw | gzip -9 | brotli -q 11 |
|---|---|---|---|
| Official default empty project | 46.24 MiB | 10.91 MiB | 7.47 MiB |
The reason an empty project is already this large is not the game assets. The main size comes from the Godot Web runtime itself: wasm, JavaScript glue code, the HTML shell, worklets, and other runtime files. The empty project’s .pck file is only a few KB, so it barely affects the total size.
Compressing With Brotli
Brotli is a general-purpose compression algorithm from Google, and it is now widely supported by modern browsers and CDNs. For Web games, it is especially useful for compressing files like wasm, js, and pck, which tend to have enough structure for strong compression.
In this empty project, the official default export is 46.24 MiB raw, 10.91 MiB with gzip, and 7.47 MiB with brotli. This step has almost no downside, as long as your server or CDN correctly serves precompressed brotli files with the right response headers.
Disabling Web Extension Support
Without rebuilding Godot, one free optimization is disabling Web extension support.
| Case | Raw | brotli -q 11 |
|---|---|---|
| Official default empty project | 46.24 MiB | 7.47 MiB |
| Export selected main scene only | 46.23 MiB | 7.46 MiB |
| Disable Web extension support | 37.98 MiB | 6.66 MiB |
This directly saves about 8.25 MiB raw. If your H5 game does not use Web GDExtension, this has almost no downside and is the first official-template optimization I would recommend.
But it also has a limit. After this, the raw size is still 37.98 MiB, and the brotli size is still 6.66 MiB. To go further, you need a custom export template.
Custom Export Templates
Godot supports custom export templates, which means you can disable modules you do not use. For example, in a 2D game, you can drop 3D nodes, 3D physics, 3D navigation, XR, and some 3D import/tooling modules. In a 3D game, you can still drop XR, 3D navigation, unused networking/multiplayer modules, some media codecs, and unnecessary importers.
I first built a general custom base:
scons platform=web target=template_release threads=no dlink_enabled=no production=yes optimize=size_extra lto=full javascript_eval=no
This template disables threads and dynamic linking/GDExtension, uses optimize=size_extra with full LTO, and disables the JavaScript eval interface.
| Case | Raw | brotli -q 11 |
|---|---|---|
| Best official template, extensions disabled | 37.98 MiB | 6.66 MiB |
| Custom base | 29.03 MiB | 6.23 MiB |
Raw size dropped by 8.96 MiB, but brotli only dropped by 0.43 MiB. This is an important point: raw size and download size do not scale linearly, because wasm already compresses very well.
Disabling 3D
For a 2D game, the most important flag is:
disable_3d=yes
This removes many 3D nodes and related subsystems at compile time, and also removes 3D physics, 3D navigation, XR, and related features.
| Case | Raw | brotli -q 11 | Use case |
|---|---|---|---|
| Custom base | 29.03 MiB | 6.23 MiB | Still includes 3D |
| 2D-only | 23.28 MiB | 5.12 MiB | Practical baseline for normal 2D games |
This saves 5.75 MiB raw and 1.11 MiB brotli. This is the most important practical optimization in the test.
If your project is a normal 2D H5 game and still needs GDScript, GUI, audio, 2D physics, or 2D navigation, the 2D-only result is the more realistic reference point: around 5.12 MiB with brotli.
UI/Clicker 2D Games Can Also Drop Physics and Navigation
Many H5 games do not need the full 2D physics and navigation stack. For example, clicker, idle, merge, or lightweight UI-driven games may not use rigid bodies, Area2D, RayCast2D, or NavigationAgent2D at all.
For these games, you can also use:
disable_physics_2d=yes
disable_navigation_2d=yes
| Case | Raw | brotli -q 11 | Use case |
|---|---|---|---|
| 2D-only | 23.28 MiB | 5.12 MiB | Keeps 2D physics/navigation |
| 2D UI-only | 21.78 MiB | 4.86 MiB | No 2D physics/navigation |
The savings are smaller than disable_3d=yes, but this is still useful for UI/clicker/simple 2D games. The tradeoff is clear: if your gameplay depends on Godot 2D physics, Area2D, RayCast2D, or navigation, you cannot use this cut.
3D Project Optimization
| Case | Raw | brotli -q 11 | Notes |
|---|---|---|---|
| Custom base | 29.03 MiB | 6.23 MiB | Custom base template |
| 3D casual | 28.44 MiB | 6.12 MiB | Removes XR and 3D navigation |
This only saves 0.59 MiB raw and 0.11 MiB brotli.
That result makes sense. A 3D game must keep 3D nodes, the 3D rendering path, mesh/material/texture structures, and quite a bit of shared rendering server code. It cannot remove the whole 3D subsystem the way a 2D game can.
So for a normal 3D Godot H5 game, around 6 MiB brotli is a more realistic target.
Can It Go Even Further?
After the previous steps, a 2D UI-only game was down to 4.86 MiB. From there, I removed many features that normal projects often need:
-
GDScript.
-
Text and advanced font-related modules.
-
Common media modules such as mp3, vorbis, ogg, theora, jpg, webp, and svg.
-
Networking, multiplayer, websocket, webrtc, and TLS.
-
2D physics, 2D navigation, 3D, 3D physics, 3D navigation, and XR.
-
Many importers and tooling modules, such as gltf, fbx, csg, meshoptimizer, and visual_shader.
This produced an extreme empty 2D shell:
| Case | Raw | brotli -q 11 |
|---|---|---|
| 2D UI-only | 21.78 MiB | 4.86 MiB |
| Extreme 2D shell | 15.02 MiB | 3.03 MiB |
At this point I wanted to see whether it could go below 3 MiB, so I added post-processing:
-
wasm-opt --all-features -Oz --strip-debug --strip-producers -
terser -c -m -
Minimal HTML shell
-
Removed the default splash PNG
-
Removed audio worklet files
The final result was 3,142,210 bytes, which is below 3 MiB using base-1024 units.
| Case | Raw | brotli -q 11 | brotli bytes |
|---|---|---|---|
| Extreme 2D shell | 15.02 MiB | 3.03 MiB | 3,182,110 |
| Post-processed lean shell | 13.71 MiB | 3.00 MiB | 3,142,210 |
One detail is worth noting: wasm-opt reduced the raw wasm size by 1,238,591 bytes, but the brotli total only went down by 685 bytes. In other words, a much smaller raw file does not necessarily mean a much smaller final download.
What actually pushed this shell below 3 MiB was the combination of JS minification, removing the splash PNG, removing audio worklets, and using a minimal HTML shell.
This is only a theoretical test. I only wanted to see how far it could go, and this version even removes GDScript, so it should not be treated as a realistic game target.
Practical Targets
Instead of focusing on the final 3.00 MiB, these are the numbers I would actually use as references:
| Type | Raw | brotli download | Recommendation |
|---|---|---|---|
| Normal 3D H5 | 28.44 MiB | 6.12 MiB | Can remove XR/navigation, but keeps 3D core |
| Normal 2D H5 | 23.28 MiB | 5.12 MiB | disable_3d=yes is the key |
| UI/clicker 2D | 21.78 MiB | 4.86 MiB | Useful if you do not need physics/navigation |
Summary
The main reason Godot Web packages are large is that the official template includes the full runtime, not that the empty project assets are large.
Within the official template, disabling Web extension support is the most useful low-risk optimization. It brings raw size down from 46.24 MiB to 37.98 MiB.
If you are willing to build a custom Web export template, the key optimization for normal 2D projects is disable_3d=yes, which brings the brotli download size to around 5.12 MiB.
If the project is a UI/clicker/simple 2D game and does not need 2D physics or navigation, it can go further to around 4.86 MiB.
The 3.00 MiB result is an extreme test. It removes many features real games normally need, and only gets below binary 3 MiB after additional post-processing.
For H5 game platforms such as CrazyGames, the package limit is 50MB, but if your game is larger than 25MB, it will not receive mobile platform recommendations, so staying below 25MB is better. Poki is even stricter, recommending package sizes below 10MB. The uncompressed raw export of an empty Godot project already exceeds that target. With the optimizations above, the empty project can be brought down to around 6MB, leaving about 4MB for core assets so players can enter the game quickly. More content can then be split into additional pck files and loaded progressively.
PS. While writing this, I discovered that this repository might be helpful for those who want to quickly apply the theories presented in this article. https://github.com/markushevpro/godot-minimize-html-build.git