Skip to content

Instantly share code, notes, and snippets.

@nisovin
Last active January 1, 2024 03:16
Show Gist options
  • Save nisovin/577e5596a36e899e8932747de1b7a353 to your computer and use it in GitHub Desktop.
Save nisovin/577e5596a36e899e8932747de1b7a353 to your computer and use it in GitHub Desktop.

Godot Threads on Itch.io

UPDATE

As of Feb 2023, itch.io supports SharedArrayBuffer on Firefox, so this document should be unnecessary.

Introduction

Itch.io has recently implemented the ability to use SharedArrayBuffer on its site, which means you can export games with Threads enabled for HTML5. You can learn more about this here. This is great for Godot, as it helps to improve/fix the sound laggyness that sometimes occurs in web builds. It is also now required for Godot 4.

Unfortunately, the current implementation only works on Chrome. If you just enable it normally, your Godot game will only run on Chrome and will fail to load on some other browsers like Firefox. There are a couple work-arounds to this: you can pop out the game into a new window, or you can include a fallback wasm that doesn't have threads enabled.

Option 1

The first, and easiest, option is to just pop out the game into a new tab when it starts. This will break it out of the iframe, which will allow Firefox to enable SharedArrayBuffer without a problem. This is also the only choice if using Godot 4, which does not have a build that works without SharedArrayBuffer.

Just add the following to the "Head Include" section of your export:

<script>
if (!window.SharedArrayBuffer && location.host == 'html.itch.zone') {
	let id = location.pathname.split('/')[2];
	let url = '//itch.io/embed-upload/' + id + '?color=303333';
	window.open(url);
	window.addEventListener('load', () => {
		document.getElementById('canvas').remove();
		document.getElementById('status').innerHTML = '<button type="button" onclick="window.open(\'' + url + '\')">Play</button>';
		document.getElementById('status').style.visibility = 'visible';
	});
}
</script>

This will check if SharedArrayBuffer is available, and if not, it will pop out into a new tab. This allows Firefox to enable SharedArrayBuffer support because it's no longer embedded in the iframe.

When you upload your game to itch.io, enable SharedArrayBuffer support in the "Embed options" section (it says SharedArrayBuffer support — (Experimental) This may break parts of the page or your project. Only enable if you know you need it.).

That should be all you need to do! If you run the game in Chrome, it will run normally. If you run it in Firefox, it will pop open a new tab instead and run there. This should also be future-proof, so if Firefox starts working on itch.io normally, it will not pop-out the window anymore.

Option 2

This method will not work with Godot 4, which requires SharedArrayBuffer (it does not have a non-threads build).

This is the fallback method. By including both wasm versions in the zip, you set up the Javascript to choose the correct one based on whether SharedArrayBuffer is available. It will fall back to the Regular non-thread version if SharedArrayBuffer is not available. This method should also be compatible with future changes, so if SharedArrayBuffer starts to work on Firefox, it should automatically work with no changes needed.

Step 1 - Export the game

First, export your game twice into the same folder, once as Regular and once with Threads. This document will assume you've exported the Regular version as game.html and the Threads version as game_threads.html.

You can go into the export folder and delete the game_threads.pck file, the game_threads.html file, and any game_threads*.png files (but leave the other game*.png files).

Step 2 - Edit the HTML file

Start by renaming game.html to index.html, which is required by itch.io. This also allows you to export again later without overwriting the changes we're about to make to index.html.

Now open your index.html file in a text editor (I usually use Notepad++). Look for this line, which should appear a little over halfway down:

  <script type='text/javascript' src='game.js'></script>

You need to delete this line entirely.

Now the very next line should look like this:

  <script type='text/javascript'>//<![CDATA[

You will need to add some code after that, so that your file now looks like this:

  <script type='text/javascript'>//<![CDATA[
	const THREADS = "SharedArrayBuffer" in window;
	if (THREADS) console.log('SharedArrayBuffer is available!');
	let script = document.createElement('script');
	script.setAttribute('src', THREADS ? 'game_threads.js' : 'game.js');
	script.addEventListener('load', function() {

This code will selectively load the correct Javascript file based on whether SharedArrayBuffer is available in the browser.

Now, the next line should currently look something like this:

const GODOT_CONFIG = {"args":[], "canvasResizePolicy":2, "executable":"game", 
"experimentalVK":false, "fileSizes": {"game.pck":8608208, "game.wasm":14301008}, 
"focusCanvas":true, "gdnativeLibs":[]};

We need to modify this line to use the correct wasm based on whether SharedArrayBuffer is available. We will also specify the pck file, so that we don't need to include both versions in the zip file later. Change it to look something like this:

const GODOT_CONFIG = {"args":[], "canvasResizePolicy":2, "executable":THREADS?"game_threads":"game", 
"mainPack":"game.pck", "experimentalVK":false, "fileSizes": 
{"game.pck":8608080, "game.wasm":14301008, "game_threads.wasm":14620719}, 
"focusCanvas":true, "gdnativeLibs":[]};

You will want to make sure those file sizes are correct for your own pck and wasm files. The wasm sizes in this example are from 3.5.beta4, and will be different if you use a different version. You can either find them in your file system, or open your other html files and copy and paste the sizes from there. Also, be sure to update the pck filesize if you make changes and re-export later!

Now, scroll down to the end of the file, where you will find something like this:

		})();
	//]]></script>

We're just going to add a bit of extra code so it looks like this:

		})();
		});
		document.body.appendChild(script);
	//]]></script>

We're now done with the edits!

Step 3 - Create the zip file

When selecting which files to put into the zip file, you need to make sure to include:

  • The index.html we just edited
  • game.pck
  • Both .wasm files
  • Every .js file
  • The game*.png files (though you shouldn't need to include the game_threads*.png files)

You do not need to include game_threads.pck, as it is the same as game.pck (this should have been deleted in step 2). You also do not need to include the other .html files.

Step 4 - Upload to itch.io

Go ahead and upload your zip file to itch.io. In your Edit project page, be sure to enable SharedArrayBuffer support in the Embed Options section (it says SharedArrayBuffer support — (Experimental) This may break parts of the page or your project. Only enable if you know you need it.). Be sure to test in both Chrome and other browsers to make sure it loads correctly.

Making Changes

If you make changes to your game, you should only need to export the Regular version, as the wasm file for the Threads versions should still be in your export folder. You should also go into the index.html file and update the file size for the new pck file. As long as you continue to export as game.html (or whatever you choose) and not index.html, your changes should remain intact so you do not need to do them again.

If you choose to change Godot versions, you will of course need to do both exports again to get the new wasm files.

Caveats

Be sure to read the information from itch.io regarding this feature. It adds a lot of browser security restrictions that may affect your game, so you will want to test thoroughly. In particular, if you are trying to download resources from other domains, those may fail unless they are set up correctly.

@jasontherand
Copy link

I just wanted to say thank you for making this guide!
I knew this should be possible, so that you for doing all the hard work figuring out how. You have saved me many hours of work. You are the best!

@bend-n
Copy link

bend-n commented Dec 26, 2022

These are good ideas, thanks for listing them out.

I made a stockfish addon, that checks for sharedarraybuffer to see if it can work, and I did this so you could have it non threaded, and the game would still work, instead of doing the sane thing and making it a engine module, but the threads/no threads option for 3.x is eye opening. Ti's a shame it doesnt work for 4.x.

The pop out option is interesting too, i was not aware that popping out the iframe enabled sharedarraybuffer.

The pop up solution doesnt work when the user blocks popups, however. Is there a way to check if the popping worked, and if not, display a warning? I have a warning when there is no sharedarraybuffer, but idk how to check if the popup worked.

@akien-mga
Copy link

For the record, leaf from itch.io got the domain enabled for Firefox's Origin Trial for the COEP:credentialless feature which itch.io uses when enabling the experimental SharedArrayBuffer support: https://wiki.mozilla.org/Origin_Trials

So projects hosted on itch.io with SharedArrayBuffer enabled should work fine on Firefox now too.

@bend-n
Copy link

bend-n commented Feb 23, 2023

Oh, thats great! Do you know what other major browsers dont support credentialless? is it just all besides chrome and ff?

@akien-mga
Copy link

See https://caniuse.com/mdn-http_headers_cross-origin-embedder-policy_credentialless - basically Safari will be lagging behind as always.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment