If you exported your Godot game to HTML5 and the exported files are much larger than the underlying project assets, then this document is for you.
Recently, I created a game in Godot 3.6 (GLES2) where the main assets were 448 images in a TileMap. I’d optimized these images so they occupied 9 MB in the Godot project folder but exporting the game to HTML5 ballooned it to 93 MB.
Many others have had the same issue.
The reason for the bloat: Godot’s internal format for images
My mistake was optimizing my images before adding them to the Godot project.
I converted my JPEG files to an 80% optimized WEBP – only for Godot to do it’s own internal conversion to PNG when I imported them as “Lossless” images. This conversion can increase your asset size by 10 times.
I eventually worked out a few rules:
- Do not pre-convert or pre-optimize your image files. Keep the asset at full-quality in your Project directory.
- Try to use the “Lossy” compression in your Import tab.
- Adjust the quality level of your asset through Godot’s “Lossy Quality” setting. Let Godot perform the optimization for you.
- Remember to press “Reimport” each time you change a setting!
- Use “Losless” imports only if your source image has to be in PNG format, then “Lossless” is a good choice (see Appendix A)
The rationale for these rules
No matter what format your image files are in, Godot will convert them to one of 4 file types:
Lossless | PNG | Doesn’t allow you to adjust the quality of your image to save on file size. |
Lossy | WEBP | Lets you control the compression level. Use the Lossy Quality slider in the Import tab. |
Video RAM | VRAM Compressed | Faster to load and use in the GPU – but takes up more space on disk |
Uncompressed | VRAM Uncompressed | EXR textures, HDR image formats |
(Additional reference for “Lossy” being WEBP and “Losless” being PNG)
The difference Compression Mode makes
To demonstrate the difference these Import settings make on the final HTML5 game, I created a sample game in Godot 3.6 (GLES2). The entire game is just 5 copies of the JPEG image below:

PCK Files
When you export your game, the .pck file is a compressed file that packages up all your code, image and audio assets. Everything that’s unique to your game is in this one file.
This is one of the two largest files in your export. The other is the .wasm file.
Each copy was imported with different compression settings. After exporting the game, I explored the resulting PCK file with the Windows program “Godot PCK Explorer” by Dmitriy Salnikov (archived version here). At a glance:

All these JPEG files are converted to a .stex file (StreamTexture), which is Godot’s internal representation for images.
This is a neater table showing the disk space every variant takes up:
Lossy, 80% quality | 46 KB | |
Lossy, 100% quality | 129 KB | Here, we are converting a JPEG to a WEBP at full quality. |
Pre-import JPEG | 225 KB | — |
VRAM, 80% quality | 256 KB | I exported Desktop + Mobile Compressed VRAM. I believe that the images won’t show without that. 128KB for Desktop + 128KB for Mobile = 256 KB in Total |
Lossless | 309 KB | Converting the JPEG into a PNG internally baloons the size by 37%. We’re better off converting to Lossy. |
Uncompressed | 768 KB | I’m not sure why this increases the size of the image so much. Email me if you have an explanation |
Setting a lower “Lossy Quality” on images boosted the startup speed of my game previews during development. I recommend that you import at a very low quality for dev and re-import at higher quality just before final publishing.
Based on the above, my recommendation is to use full-quality assets in your Godot working folder. You can adjust their quality downwards later using the Import tab. By doing that for my project – putting full-size JPEGs in the working folder but importing them as 80% lossy – my HTML5 export went down from 93 MB in size to 33 MB.
Side note: how to re-import many images at once
To change your import/compression settings for many images at one time, go to the FileSystem area and select multiples by pressing down the Shift button and clicking around. Then, you can change the Import settings and click Reimport. Note how the screenshot below indicates “13 Files” selected:

Can I save space by using .SVG files?
Godot flattens .svg files from a vector to a raster format. That means that you lose all the filesize and infinite-scalability benefits of this file format.
As per the Godot 3.6 Documentation:
SVGs are rasterized using NanoSVG when importing them. Support is limited; complex vectors may not render correctly. For complex vectors, rendering them to PNGs using Inkscape is often a better solution.
Godot 4.4 uses ThorVG for rasterizing SVGs. But the result is the same: you don’t gain anything by using vectors over .png or .jpeg files.
When an image starts out as an SVG, its exported size on disk does not change based changes to its dimensions in the Editor.
My experimentation showed that a simple 3 KB .svg file increased to 17 KB in the .pck file, while a complex 89 KB .svg file became 85 KB in the final export (but quality suffered).
Turn off “Export with debug”
When exporting the final project – at the very last step – you’ll see a discreet “Export with Debug” checkbox that’s on by default. Check it to “off” when publishing the final version of your game.
This removes debug features/symbols from the final game. Unchecking the box made my game go from 38.5 MB in size to 32.8 MB – a 15% space savings.

Get rid of orphan assets
Godot 3.6 has a basic feature that shows you game assets that aren’t being referenced from anywhere. Go to Project > Tools > Orphan Resource Explorer to see these “orphan assets”. You can then delete them from your game to free up space in the final .pck export.

Advanced: shrinking the WASM file
The two biggest files in your game export are the .pck file (containing all your unique assets) and the .wasm file.
The .wasm file is the Godot game engine. It is the universal “base” code that performs all the functions that your game relies on. The .wasm file is about 20MB in size.
You have two options if you want to shrink the size of the game engine itself.
Option 1: compress the WASM file
(This section only applies if you host your own game. If you plan to host your game at Gitlab/Github/Itch.io then skip this.)
First, we need to make sure that .wasm files are served with a MIME type of application/wasm to take advantage of certain startup-time optimizations (source: “Exporting for the Web“)
If your hosting provider uses CPanel, you can set up a custom MIME type definition by going to “Advanced -> MIME Types”.

Now we can move on to compressing the .wasm file.
Compress “on the fly”
You can set up “on the fly” .wasm compression and it will serve a smaller file to visitors, at the cost of higher CPU usage. If you expect lots of players then skip this section and go to the next one.
On CPanel, you can set this up by going to the “Optimize Website” section. Go to the “Manage Compression” area. In “Compress the specified MIME types” set up application/wasm as one of the compressed types.
If you can’t find “Optimize Website” then your host probably has it disabled.
Pre-compress
You can compress the .wasm file ahead of time, and have your web server return the compressed version whenever it is available.
First, compress your .wasm using the gzip algorithm. Save it with a .gz extension (so if your original was index.wasm then the compressed file is index.wasm.gz):
Upload the .wasm.gz file to your server, into the same folder as your original .wasm file.
Add this code to your Apache .htaccess configuration:
<IfModule mod_headers.c> # Serve gzip compressed WASM files if they exist # and the client accepts gzip RewriteCond "%{HTTP:Accept-encoding}" "gzip" RewriteCond "%{REQUEST_FILENAME}\.gz" "-s" RewriteRule "^(.*)\.wasm" "$1\.wasm\.gz" [QSA] # Serve correct content types, and prevent double compression. RewriteRule "\.wasm\.gz$" "-" [T=application/wasm,E=no-gzip:1] <FilesMatch "(\.wasm\.gz)$"> # Serve correct encoding type. # Header append Content-Encoding gzip <--- was doubling up the gzip content encoding value Header set Content-Encoding gzip # Force proxies to cache gzip & # non-gzip wasm files separately. Header append Vary Accept-Encoding </FilesMatch> </IfModule>(adapted from https://httpd.apache.org/docs/2.4/mod/mod_brotli.html#precompressed )
Now, when a visitor requests a .wasm file, the server will first check for the existance of a .wasm.gz variant – and would serve up the gzipped file if available.

This optimization shrank my index.wasm file from 19 MB to 5 MB – a 73% decrease.
Option 2: recompiling Godot
The second thing you can do to shrink the game-engine is to cut out unnecessary features from Godot. For example: if your game is in 2D, then you can safely remove all the 3D capabilities of the Godot engine.
Recompiling Godot is beyond my skill level.
Check out this detailed post by Popcar on which Godot features are safe to cut out. The actual recompilation process uses the “scons” tool – Popcar links to a post with some details, but it’s probably best to start with the official Godot documentation on “building” the application.
These are adjustments I had to make to my HTML5 export in order to get my HTML5 game working well on mobile.
Keep Lossy files under 16,383 x 16,383
Godot stores “Lossy” files as .webp behind the scenes. The .webp file format definition limits images to a maximum size of 16,383 by 16,383 pixels. You will run into problems if you import a Lossy file bigger than this into Godot.
Animation shows up as a black box? Image size limits on mobile
Many mobile device processors can only handle images smaller than 4,096 by 4,096 pixels. If your images or animations are showing up as black boxes, then it might be because your sprite-sheet image is bigger than that on a dimension.
The solution is to get your sprite-sheet smaller than 4,096px on either side. You can do that by cutting out frames from the animation. If your sprite-sheet is long and narrow, you can also rearrange it to be a snaking rectangle.
Going from this sprite layout:

To this sprite layout:

See this thread on the r/godot subreddit for more information.
Huge background images? Split them into a TileMap of smaller ones
If your game requires gigantic background images, consider splitting them up into into 512×512 tilemaps.
You would first use a tool like imagemagick (Windows downloadable) to slide your files into a grid of equally-sized 512×512 images. Below is the code I used to split up a “terrain.jpg” file into smaller tiles that have the X and Y offset in their name:
magick terrain.jpg -crop 512x512 -set filename:tile "%[fx:page.x/512]_%[fx:page.y/512]" +repage +adjoin tile_%[filename:tile].jpgThen, using ChatGPT, I (we?) created a Godot tool that reads a folder that contains these tiles, and populates a TileMap Node with the correct image tiles – placing them in the offset defined in the name of the file.
To reduce the number of tiles in the final export, I deleted “empty” .jpg tile files from the folder. We don’t need to place tiles that are not visible in the game anyways…
TileMaps on mobile: shrink your “Quadrant Size” to 1
Does your HTML5 game get into an infinite loading loop on mobile devices? Does it display a message like “HTML5 canvas appears to be unsupported in the current browser.“?
I was running into this problem and it was related to the TileMap nodes that I had in my game.
Set your TileMap’s “Quadrant Size” to 1 if you want to improve performance on mobile devices:

What is “Quadrant Size”? According to Godot’s documentation:
A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. rendering_quadrant_size defines the length of a square’s side, in the map’s coordinate system, that forms the quadrant. The default quadrant size of 16 groups together 16 * 16 = 256 tiles.
By default, Godot will instruct your device to deal in 16 x 16 tile blocks. I don’t know about your device, but my crappy cracked-screen Android could only handle 1 x 1 blocks. Even when I had “Quadrant Size” set to 2, I was getting infinite loading loops…
Audio jitter in HTML5 games
If your background music experiences jitter or cuts out randomly, increase the audio latency in your project.
Go to Project > Project Settings > Audio. Click the actual “Audio” heading and set a “Output Latency.web” to 80 . This will increase the size of the audio buffer so playback is not choppy. I couldn’t find the original post where I saw this, but a person experimented with different latency settings an the jitter went away at the 80 level. This level solved my problem, too.

That’s it
Go forth and make games!
If you see a mistake in what I wrote (unlikely because I am perfect), or have your own tips that I can link to/add, then reach out to me at my first name at this site. I want to hear from you.
Appendix A: .stex sizes for an imported PNG file
I tested the impact of different import settings on PNG files, the same as I did with JPEGs. The original .png file was 112 KB in size.

Lossy, 80% quality | 8.4 KB | |
Lossy, 100% quality | 26.3 KB | Converting the PNG to a WEBP at full quality |
Lossless | 63.9 KB | — |
VRAM, 80% quality | 64 KB | I exported Desktop + Mobile Compressed VRAM. I believe that the images won’t show without that. 32KB for Desktop + 32KB for Mobile = 64 KB in Total |
Original PNG | 112 KB | |
Uncompressed | 256 KB |
Note that, for PNG, the Losless setting reduces it’s size (while bloating up the size of a JPEG). I believe that’s because an Godot’s behind-the-scenes conversion from PNG to PNG is much more efficient than one from JPEG to PNG.
For another comparison of sizes, see this post by user Denxel on Reddit.
Appendix B: code for a Godot Editor tool that populates a tilemap
Notes:
- This is a “tool” and it runs only in the Godot Editor itself
- This tool script is attached to a Node2D that’s a child of the main scene’s root node. It only fires when you flip away from the main scene’s 2D view and back again.
- The code populates 2 nodes with 2 sets of tiles.
- One Godot Node is supplied in “terrain_path” and another in “canopy_path”
- The res://assets/level/terraintiles path is the folder where all the “Terrain Tiles” 512×512 .jpg tiles are stored
- For any complaints, email my developer, Chad G. Petey at 123 Main St. Townsville, OH.