Fan-made · Free in PC browser · Android on Google Play · iOS coming soon · Not affiliated
The download is only part of startup
A “slow web build” often sends us straight to the export size. Cut bytes, reload, hope it feels faster. But startup includes several serial dependencies: getting the files, preparing the engine, loading resources, and drawing the scene. Download size directly describes only the transfer part; it cannot explain the whole wait.
Our CGT measurements made that distinction useful. The engine could start while the game still had another pack to fetch. Then our own loader kept covering the scene. Before changing the export, we needed to know which finish line we meant.
The four phases to separate
Use this sequence as a diagnostic map, not a set of durations to add blindly: download → wasm compile/instantiate → pack load/mount → first frame. Streaming compilation can overlap downloading, and our runtime pack adds another download after engine startup.
-
Download:
.wasmcontains the engine,.pckcontains game resources, and.jsstarts it; payload and connection affect this stage, so inspect export contents and the template. - Compile/instantiate: the browser prepares the engine module; its payload and enabled features are relevant, making an optimized template a candidate lever.
- Pack load/mount: getting the pack's bytes is distinct from making its resources available; inspect pack contents and loading order.
- First frame: engine and scene preparation must reach a draw; inspect scene startup and any loader that hides the result.
Godot's web export documentation explains the file roles and template considerations. A canvas draw, a ready scene, and a visible game are separate milestones in our build.
Measure the boundaries before changing the build
You can start without changing game code:
- Open DevTools Network, enable Disable cache, and reload for a first-visit check. Record file sizes and request times. Use the Wasm filter for the engine, then clear it to inspect packs and scripts.
- Uncheck Disable cache and load again to populate the cache; reload afterward for a repeat-visit comparison. Check which requests actually use cache.
- Use Performance record-and-reload. Inspect Main and other tracks for compilation activity and the first frame. Labels vary by Chrome version; avoid treating a particular label as universal.
The Network reference distinguishes delivered size from decoded size and defines request time through the final byte. The Performance reference covers recording page loads.
Our existing measurements went further: Chrome 156 headless, driven through Puppeteer and the Chrome DevTools Protocol, on a fast datacenter link without throttling. There was no hardware GPU; WebGL used SwiftShader software rendering. First visits used an empty profile; repeats opened another tab in the same profile with cache enabled. We used empty profiles instead of disabling cache so the initial visit could populate it.
Network events supplied byte counts and request durations. Existing cgtTrace marks supplied engine-started, pack-mounted, host-ready, and reveal timestamps. Instrumentation wrapped instantiateStreaming and canvas drawing methods; Chrome wasm traces captured compilation. This detailed capture used instrumentation beyond the manual steps above.
Each timing below is the median of 3 runs. Milestones are milliseconds from navigation; request durations and gaps are elapsed milliseconds.
| Measurement | First visit | Repeat visit |
|---|---|---|
index.wasm request duration |
690 ms | 230 ms |
| Engine started | 2,647 ms | 1,410 ms |
Runtime main.pck request duration |
988 ms | 696 ms |
main.pck mounted |
6,524 ms | 3,647 ms |
| Pack last byte → mounted | 2,144 ms | 1,121 ms |
Scene ready (host-ready) |
7,072 ms | 4,372 ms |
| Loader overlay removed | 11,147 ms | 9,619 ms |
On the first visit, index.wasm was 39.5 MB decoded (39,514,754 bytes), downloaded in that median 690 ms. But after engine startup, our game fetched a second pack: main.pck, 50.9 MB decoded (50,860,652 bytes). Its mount completed about 2.1 s after its last byte arrived.
Scene readiness was not the reveal. Our overlay waited for the intro video, leaving a median 4,506 ms between host-ready and reveal. These gaps are medians of per-run differences, not differences between independently calculated milestone medians.
On repeats, wasm and main.pck came from disk cache with 0 bytes transferred, yet mounting still finished about 1.1 s after the bytes. Lazy compilation counts were essentially unchanged: 14,207 versus 14,208, with summed durations about 0.5 s in both cases. Those sums are compilation work, not another wall-clock phase to append.
The longest pieces here were pack mounting and our loader choreography, not the wasm download. This shows startup's shape, not player experience: we did not measure real-GPU rendering or time to first player input.
Three levers beyond compression
Pack contents and export filters. When it's worth it: the timeline points toward pack transfer or resource preparation. First list the biggest files inside the pack and check why they ship. We have not measured our pack contents, so we cannot name a resource culprit. Export resource modes and filters let you select resources and dependencies and control included or excluded files.
A custom export template and build profile. When it's worth it: engine payload or initialization warrants investigating unused features, and you can maintain your own template. Size optimization describes disabling modules; retain theora for video or ogg/vorbis for music when used. The configuration editor can detect features and save a build profile, but nothing shrinks until you compile the template. Detection can miss features in runtime-loaded PCKs—directly relevant to our main.pck.
Threads versus single-threaded export. When it's worth it: evaluate performance alongside hosting requirements. Since Godot 4.3, single-threaded exports need neither SharedArrayBuffer nor COOP/COEP isolation. Threads perform better but require isolation and a secure context. See the official web export update. Our console reports a single-threaded build, yet the host sends Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp. Check what your host actually sends.
We have not measured the effects of any of these levers on our build. Transfer compression is a separate topic.
Why we don't claim mobile browser numbers
Godot's web documentation requires WebGL 2.0 with Compatibility rendering and isolation for threads. It says native mobile exports perform better, CPU/GPU resources are at a premium on mobile, and optimized templates are recommended for mobile loading times.
Those are documented considerations, not results from our game. We did not measure mobile browsers and make no mobile performance claims. The game is free in the PC browser; Android is on Google Play; iOS is coming soon.
Cut the longest phase you measured
Measure first, then target the longest phase. Size describes one part of startup; a completed download does not mean your player can see the game.
Which phase is the longest on your Godot web build's first visit?
PC browser: https://cgt.monster/x/devto9
Top comments (0)