# Godot

> Export a Godot 4 project for the web and upload it to Fradiation Games. Untested on the site; it follows Godot's web export requirements and Fradiation's file rules.

Godot exports to the web as a folder of static files. Upload the folder.

> [!NOTE]
> No Godot game has shipped on Fradiation yet. This guide follows Godot's own web export requirements and our file rules, and the SDK example is untested on the site. If an export won't upload or won't run, `node check-build.mjs <folder>` explains most problems.

## Export

1. Install the export templates that match your editor version: **Editor → Manage Export Templates → Download and Install**.
2. Open **Project → Export**, choose **Add**, then **Web**.
3. Set the export path to a folder, with the HTML file named `index.html`, for example `build/index.html`. That puts `index.html` at the top of the build. Godot names every other file after the HTML file (`index.js`, `index.wasm`), so keep the names it gives them.
4. Export the project.
5. Put `fradiation.json` in the export folder, next to `index.html`. Godot doesn't write it.

   ```json
   {
     "$schema": "https://www.fradiation.games/fradiation.schema.json",
     "viewport": { "width": 1152, "height": 648 },
     "threads": false
   }
   ```

   `viewport` sets the aspect ratio the cabinet letterboxes to. 1152×648 is Godot 4's default window size. See [fradiation.json](/docs/fradiation-json).

If your HTML file has another name, set `entry` in `fradiation.json` to that name.

### Files in the export

A Godot 4 web export usually contains these files. The set varies with the Godot version and your export options.

| File | Notes |
| --- | --- |
| `index.html` | The page. |
| `index.js` | Engine startup code. |
| `index.wasm` | The engine. |
| `index.pck` | Your game data. |
| `index.png` | Splash image. |
| `index.audio.worklet.js` | Audio. Newer versions also write `index.audio.position.worklet.js`. |
| `index.icon.png`, `index.apple-touch-icon.png` | Written when the Export Icon option is on. |
| `index.side.wasm` | Only with Extensions Support. |
| `index.manifest.json`, `index.service.worker.js`, `index.offline.html` | Only with Progressive Web App on. It is off by default, and untested here. Leave it off. |
| `index.worker.js` | Godot 4.0 to 4.2 only. |

Every one of these has an allowed extension (`html`, `js`, `wasm`, `pck`, `png`, `json`). Run `node check-build.mjs build` on the folder to confirm. See [Builds and uploads](/docs/builds#check-a-build-first).

## Threads

Use Godot 4.3 or later and leave the export preset's **Thread Support** option off (it's off unless you turn it on). A single-threaded export needs no `SharedArrayBuffer`, and `"threads"` stays `false`.

> [!WARNING]
> Threaded builds don't run on the site yet: the game pages that frame them aren't cross-origin isolated, so `SharedArrayBuffer` is unavailable inside the cabinet. Godot 4.0 to 4.2 have no single-threaded web export, so games on those versions can't run here until that changes. See [Threads](/docs/builds#threads).

## C# projects

Godot 4 doesn't support exporting C# projects to the web. Godot's web export docs say so. Check them for your version before you start.

## Command line export

```bash
mkdir -p build
godot --headless --export-release "Web" build/index.html
```

`"Web"` is the name of the export preset. A relative output path is relative to the folder that holds `project.godot`, not to the current directory. Pass `--path <project folder>` to run from elsewhere. For a full build script, see [Batch builds](/docs/agents#batch-builds).

## Filling the frame

The game runs in an iframe. The cabinet letterboxes it to your `viewport` and expects the game to fill its window.

- In the Web preset, leave **Canvas Resize Policy** on **Adaptive**, its default. Godot then sizes the canvas to the whole browser window, which here is the frame. The default HTML shell fills the window.
- Set the project's stretch settings so the game scales: **Project Settings → Display → Window → Stretch**. Choose a Mode (`canvas_items` for 2D, `viewport` for pixel art) and an Aspect (`keep` or `expand`).
- Don't use **None** unless your own HTML shell sizes the canvas.

## Calling the SDK from GDScript

Godot reaches JavaScript through `JavaScriptBridge`. The SDK sets `window.fradiation`, so load it in the page first.

1. Download [fradiation-sdk.js](https://www.fradiation.games/sdk/fradiation-sdk.js) into the export folder, next to `index.html`.
2. In the Web preset, under HTML, set **Head Include** to:

   ```html
   <script src="fradiation-sdk.js"></script>
   ```

   Godot adds Head Include to the `<head>` of the exported page.
3. Copy `fradiation-sdk.js` into the folder again if you clear the export folder before exporting.

Save this as `fradiation.gd` and add it as an autoload named `Fradiation` in Project Settings. This example is untested on the site.

```gdscript
extends Node

var _fradiation

func _ready() -> void:
	if OS.has_feature("web"):
		_fradiation = JavaScriptBridge.get_interface("fradiation")
		if _fradiation:
			_fradiation.ready()

func unlock_mutation(key: String) -> void:
	if _fradiation:
		_fradiation.unlockMutation(key)

func submit_score(board: String, value: float) -> void:
	if _fradiation:
		_fradiation.submitScore(board, value)

func track(event_name: String) -> void:
	if _fradiation:
		_fradiation.track(event_name)

func ask_for_feedback(prompt: String = "") -> void:
	if _fradiation:
		if prompt.is_empty():
			_fradiation.askForFeedback()
		else:
			_fradiation.askForFeedback(prompt)
```

Call it from anywhere:

```gdscript
Fradiation.unlock_mutation("boss-down")
Fradiation.submit_score("fastest-clear", elapsed_seconds * 1000.0)
```

- `JavaScriptBridge.get_interface("fradiation")` returns the `window.fradiation` object. Godot has no `get_window()` method.
- `OS.has_feature("web")` is false in the editor and on desktop, so those calls do nothing there. Godot's docs recommend this guard.
- Declare every mutation and board key in `fradiation.json`. A board with `"format": "time"` takes milliseconds.
- The SDK methods return promises. The example ignores them. Reading a result needs a callback made with `JavaScriptBridge.create_callback`, which Godot calls with one Array argument. This guide doesn't cover it.
- Outside the site, the SDK resolves every call with defaults. See [SDK](/docs/sdk).

## Upload

Upload the export folder, or a zip of it, on `/dev/<slug>`. To check and zip in one step, [download check-build.mjs](https://www.fradiation.games/skills/fradiation/check-build.mjs) (Node 20 or later):

```bash
node check-build.mjs pack build Godot.zip
```

See [Publish a game](/docs/publish) for the upload steps.

## Troubleshooting

| Symptom | Cause | Fix |
| --- | --- | --- |
| Blank frame or 404s | `index.html` isn't at the top of the upload, or the HTML file has another name. | Export to `index.html`, or set `entry` in `fradiation.json`. |
| Godot's start page lists missing features such as cross-origin isolation or `SharedArrayBuffer` | The export has Thread Support on. Threaded builds don't run on the site yet. | Export without thread support (Godot 4.3 and later). |
| The game doesn't fill the frame | Canvas Resize Policy is **None** or **Project**, or the stretch settings are off. | Set it to **Adaptive** and set the stretch Mode and Aspect. |
| The SDK does nothing | Head Include is empty, `fradiation-sdk.js` isn't in the export folder, or the game runs outside the site. | Check both. On the site, open DevTools, switch the console to the game's frame and run `await fradiation.ready()`. `connected: true` means the handshake worked. In a draft, a successful call shows a toast. No toast means the site rejected the call. |
| An upload is refused with "file type not allowed" | A file with an extension off the allowed list. | Remove it, or run `check-build.mjs` to list every problem. |
| No sound until the player clicks | Browsers block audio until the player interacts. | Godot's docs suggest asking the player to click, tap or press a key first. |
