Claude Code and Godot: build a browser game
Claude Code works with Godot through project files, terminal commands and an optional MCP server. Start with GDScript, build a tiny game in short loops, and make the web export part of the first milestone. A game that runs in the editor has one more jump to make: the browser.
This path builds a single-screen platformer: move, jump, collect three coins, reach an exit, restart. All tool and export facts were checked on October 1, 2026. For agent installation and sign-in, use the Claude Code guide or Codex guide.
The AI game development guide covers the general process, the engine comparison covers engine choice, and the MCP guide’s engine section compares the Godot connections. Here, pick one connection and take a project all the way to its web build.
1. Create an empty Godot project for the browser
Use Godot 4.7.2, the stable download listed by godotengine.org on the review date. Choose the standard build and GDScript for this exercise. The current web export docs say: “Projects written in C# using Godot 4 currently cannot be exported to the web.” The feature reference also directs C# web users to Godot 3.
Create a new project in an empty folder, as in Godot’s setup tutorial. Choose Compatibility as the renderer. Godot’s renderer overview lists it as the only one of the three that supports Web, so the editor and the web build use the same renderer. Name the folder coin-hop and open your agent’s terminal in it.
The commands below assume the editor executable is available as godot on PATH. Replace it with your binary’s path if needed; on macOS the executable is inside Godot.app/Contents/MacOS/. These paths and --version are documented in the command-line tutorial. Check it before proceeding:
godot --version
2. Connect Godot MCP, or keep the terminal loop
Use Coding-Solo/godot-mcp for this path. Its README requires Godot, Node.js 18 or newer, npm and an MCP-capable client; it is MIT-licensed. The README gives no general minimum Godot release. Its UID tools specifically require Godot 4.4 or newer, so don’t turn that into a promise about every tool.
Pin 0.1.1 in the commands below. Versions before it passed an unsanitized project path to a shell; the GitHub advisory says 0.1.1 fixes this by using execFile() instead of exec(). Check later releases before changing the pin.
Replace /absolute/path/to/godot with your actual executable. GODOT_PATH overrides the server’s automatic detection (README). Choose the command for your client. In Claude Code the server name goes before --env: --env takes several KEY=value pairs and rejects a name that follows it directly (Claude Code MCP docs). In both clients the launch command comes after -- (Codex MCP docs):
# Claude Code, from the game folder
claude mcp add --scope project godot --env GODOT_PATH="/absolute/path/to/godot" -- npx -y @coding-solo/godot-mcp@0.1.1
# Codex alternative
codex mcp add godot --env GODOT_PATH="/absolute/path/to/godot" -- npx -y @coding-solo/godot-mcp@0.1.1
In Claude Code, --scope project writes the server to .mcp.json in the game folder, and the next interactive session asks you to approve it. claude mcp get godot shows whether it connects (Claude Code MCP docs). Restart your client, then send:
Use get_godot_version and get_project_info on this project’s absolute path. Report the executable version and project summary. Then read project.godot and scene files through normal file access to identify the main scene and scene paths. Do not change files yet. If the connection fails, show the error before trying another path.
The server’s tool list includes create_scene, add_node, save_scene, launch_editor, run_project, get_debug_output and stop_project. Use run_project, read get_debug_output, then stop_project before launching a new run. Open the editor with launch_editor when you want to inspect the scene.
Without MCP, the agent can edit .gd scripts and .tscn text scenes, then run Godot and read terminal errors. Godot documents human-readable scene/resource formats in its feature list and automation in the CLI reference. This is enough for the import, parse, run and export commands below; MCP is a convenience, not the build system.
3. Copy project rules for the agent
Save these rules as AGENTS.md beside project.godot. Codex reads it as project guidance (Codex docs). Claude Code 2.1.277 and later read it too when the project has no CLAUDE.md. If you keep a CLAUDE.md, put @AGENTS.md at its top so both agents follow one file (Claude Code docs).
These are conventions for this project. Godot’s typing guide supports typed variables, parameters and return values; its signals tutorial shows event connections. The directory layout below is our choice, not an engine requirement (project organization).
# Coin Hop project rules
- Engine: Godot 4.7.2 standard; Compatibility renderer; GDScript 2.0.
- Type variables, parameters and return values. Use Godot 4 APIs.
No Godot 3 syntax, C#, native extensions or Thread/WorkerThreadPool work.
- Scenes: scenes/main.tscn, scenes/player.tscn, scenes/coin.tscn.
Scripts: scripts/. Source assets: assets/. Rule tests: test/unit/.
- Use snake_case files/functions and PascalCase node names.
Player root: CharacterBody2D. Coin root: Area2D. UI: CanvasLayer.
Keep documented node paths stable; report any scene-tree change.
- Use signals for pickup, death, completion and UI updates.
Poll movement input in _physics_process; do not poll gameplay events.
Each coin awards once. A run completes once. Restart resets all state.
- Configure move_left, move_right, jump and restart in Input Map.
Keyboard and touch use those same actions. No hover-only actions.
- Never hand-edit .godot/ or *.import files.
Change source assets/import settings and let Godot reimport them.
Keep generated *.import metadata in version control; ignore .godot/.
- Use res:// paths for project resources. Keep exports in build/web/;
place an empty build/.gdignore so builds are not imported/exported.
- After changes, run from the project root:
godot --headless --path . --import
godot --headless --path . --check-only --script res://scripts/player.gd
godot --headless --path . --quit-after 180
Repeat the parse command for each changed script, with its real path.
- Once GUT is installed, run the rule tests after the import:
godot --headless --path . --debug --script addons/gut/gut_cmdln.gd -gdir=res://test/unit -gexit
- Then run visibly: godot --path .
Check start, movement, jump, pickup, death, win and restart.
Inspect a screenshot and read errors; a headless pass is not visual QA.
- Web preset: Thread Support off, Extensions Support off, PWA off.
Export and play in the browser early, including a plain iframe.
- Finish each task with changed files, actual check results and limits.
Godot creates *.import metadata and imported resources itself; the import docs say to version the metadata and leave .godot/ out. That distinction matters: “don’t hand-edit” doesn’t mean “delete.” A .gdignore excludes a folder from imports and export (import docs, organization); res:// starts at the project root (paths).
4. Build one playable slice per prompt
Start with movement and a floor, then add pickups and the ending. Use simple shapes until the loop works. The prompts define the game we want, rather than promising an agent will finish it in one turn.
First prompt: movement.
Create scenes/main.tscn and scenes/player.tscn under the project rules. Main has a floor, a Player instance and a fixed camera. Player is a CharacterBody2D with a visible shape and collision. Configure arrows and A/D for movement, Space for jump. Set main.tscn as the main scene. Done when I can run, jump, land and fall to a reset point.
Run import, parse and headless checks, then open the game visibly. Report errors and the scene tree.
Use this scene contract for the next slice; the exact shapes and positions can change:
Main (Node2D)
World (Node2D)
Ground (StaticBody2D with a CollisionShape2D)
Player (instance of player.tscn)
Coins (Node2D holding coin.tscn instances)
Exit (Area2D with a CollisionShape2D)
Camera2D
HUD (CanvasLayer)
Counter (Label)
Result (Control, hidden until completion)
TouchControls (Node2D)
Second prompt: one complete run.
Add three coin instances. Each emits collected once and disables its collision before it can award again. Main owns the run state and connects signals once. Update the counter from a signal, unlock Exit after all coins, and show a result panel when the player enters the unlocked exit. Restart recreates the run, coins and counter.
Done when entering Exit early does nothing, a coin cannot award twice, and repeated restarts never multiply signal connections. Run the checks, then play those cases.
Run, read the error, fix the cause
After each prompt, run the checks in order. These flags come from Godot’s CLI reference:
godot --headless --path . --import
godot --headless --path . --check-only --script res://scripts/player.gd
godot --headless --path . --quit-after 180
godot --path .
--import waits for imports and exits. --check-only parses the supplied script; it doesn’t play the scene or check every file. --quit-after 180 ends after iterations, not 180 seconds. Read the output as well as the exit status; a quiet startup says nothing about an untouched exit trigger.
Give the agent the actual error, file and line. For example:
The parser reports “Cannot infer the type” in player.gd. Read that line and the value it receives. Add the correct explicit type, rerun the same parse command, then the startup check. Preserve the movement behavior and show the result.
For visuals, use a screenshot from the visible run and check the player, floor and HUD at game size. With MCP, pair run_project with get_debug_output; its run/debug tools are documented, but the README doesn’t list a screenshot tool (README). Have a person or a separate browser tool capture the exported game. Playtesting with AI covers the browser loop.
5. Add a test for the rule that ends a run
Use GUT 9.7.1 for this Godot 4.7.x project. The README’s version table maps that release to 4.7.x and states the MIT license. Download the 9.7.1 release, copy addons/gut into the game’s addons/ directory and enable the GUT plugin, as the README describes. GitHub marks 9.6.1, the Godot 4.6 build, as Latest, so pick by engine version.
Ask the agent to extract scripts/run_state.gd, extending RefCounted. A new run has coins: int = 0 and finished: bool = false. Its collect_coin() -> void stops at three; try_finish() -> bool succeeds once, only after three coins.
Keep duplicate-pickup protection in the coin scene too. Save this rule test as test/unit/test_run_state.gd:
extends GutTest
const RunState = preload("res://scripts/run_state.gd")
func test_exit_requires_coins_and_finishes_once() -> void:
var state: RunState = RunState.new()
assert_false(state.try_finish())
state.collect_coin()
state.collect_coin()
assert_false(state.try_finish())
state.collect_coin()
assert_true(state.try_finish())
assert_true(state.finished)
assert_false(state.try_finish())
state.collect_coin()
assert_eq(state.coins, 3)
func test_new_run_is_empty() -> void:
var state: RunState = RunState.new()
assert_eq(state.coins, 0)
assert_false(state.finished)
Run it after import. Godot owns --headless, --path, --debug and --script; GUT owns the -g options (Godot CLI, GUT command line):
godot --headless --path . --debug --script addons/gut/gut_cmdln.gd -gtest=res://test/unit/test_run_state.gd -gexit
GUT returns 0 when all tests pass and 1 when any fails; pending tests don’t change that (GUT command line). Run --import first. If GUT’s classes haven’t been imported, gut_cmdln.gd prints an error and quits with 0 before running a single test (9.7.1 source), so a green exit code alone proves nothing.
This tests the rule; separately walk into the same coin twice, enter the exit early and restart in the visible game. Scene connections and collision behavior need that check too.
6. Export a Web preset before adding polish
Install the 4.7.2 export templates through Editor > Manage Export Templates, then add a Web preset in Project > Export and name it Web. Templates and preset setup are documented in Exporting projects. Keep this preset in export_presets.cfg so later exports use the same settings.
For this GDScript game, set these explicitly. The Web platform reference documents what each option changes:
| Option | Set it to | Reason for this path |
|---|---|---|
| Thread Support | Off | No thread support or SharedArrayBuffer isolation requirement |
| Extensions Support | Off | This project uses no GDExtension |
| Progressive Web App > Enable | Off | Start with ordinary hosted files and an iframe |
| Canvas Resize Policy | Adaptive | Fit the canvas to its web page |
In export_presets.cfg, the first one reads variant/thread_support=false; an agent can check it there.
Create build/web/ and the empty build/.gdignore before exporting. From the game root:
godot --headless --path . --export-debug "Web" build/web/index.html
# After browser checks pass:
godot --headless --path . --export-release "Web" build/web/index.html
The preset name must match exactly and the output directory must already exist (CLI). Export as index.html from the start and keep the generated companion filenames together (web docs). Serve that folder through a local HTTP server for testing. With the Node and npm prerequisites above, Vercel’s serve does it in one command; npm’s --yes skips the install prompt (npx docs):
npx --yes serve build/web
Open the local URL the server prints. Play the debug export, inspect the browser console, then repeat on the release export.
What enabling threads changes
Thread Support on permits multithreading and lower-latency Stream audio (web docs). The option reference requires cross-origin isolation and warns that disabling threads can expose performance/audio issues. Measure your game before switching it on; choose the delivery environment alongside the performance decision.
Godot introduced single-threaded Web export in 4.3; its web-export report explains the distribution problem that motivated the change. The current web docs call single-threaded export the preferred default.
A threaded build uses SharedArrayBuffer. Godot requires a secure context and these response headers (serving docs):
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
The server has to send them as HTTP response headers; an HTML meta tag doesn’t count. MDN explains that cross-origin isolation also depends on permissions: the cross-origin-isolated permissions policy defaults to self, so a frame from another origin needs the embedding page to grant it. The embedding page has to be isolated as well, which is why itch.io’s option changes its own game pages too (next section).
Godot documents a PWA service-worker workaround for hosts without configurable headers (web docs). Treat that as a separate delivery route to test. It is not evidence that a threaded build will work inside any third-party iframe.
7. Upload to itch.io or embed on your own site
For itch.io, zip the contents of build/web/ with index.html at the archive root. Pick HTML Game as the kind of game, upload the zip, and set the viewport size under Embed options. itch.io requires relative asset paths and case-correct filenames (HTML5 upload docs). Keep the single-threaded build’s SharedArrayBuffer support setting off.
For a threaded build, itch.io’s own developer update documents Embed Options > Frame Options > SharedArrayBuffer support. It adds isolation headers to the game’s files, its itch.io page and Game Embed pages, and moves the game to another CDN domain, so saves kept in local storage from earlier plays stay behind.
itch.io supports play on the project page and through its Game Embed feature, not raw CDN URLs. The setting prepares itch.io’s environment; it doesn’t configure a different site’s parent page.
For a build hosted on your own HTTPS origin, use a small wrapper like this; replace the example URL. This assumes the host permits framing and serves all generated files. The iframe reference documents the fullscreen permission. With Thread Support and Extensions Support off, the game needs no cross-origin isolation, only HTTPS (web docs, export option):
<iframe
title="Coin Hop"
src="https://play.example.com/coin-hop/index.html"
width="960"
height="540"
style="width:100%;aspect-ratio:16/9;height:auto;border:0"
allow="autoplay; fullscreen"
allowfullscreen>
</iframe>
Test the actual wrapper on the actual host. Give it visible keyboard focus on click, a Start button, touch controls and a way to restart. Verify resize, audio after Start and fullscreen after a button press. Single-threaded export removes the isolation requirement; it cannot repair a missing file or a host that blocks iframes. If you add saves later, Godot’s docs note that user:// data in an iframe persists only when the player allows third-party cookies (web docs).
Keep download size visible
Measure your release build’s actual network transfer before adding art. Record the .wasm, .pck and asset requests with cache disabled, then test a repeat visit. Use transfer bytes and startup time for the device/connection you tested, not the zip’s size as a loading-time claim.
The export manual recommends server compression for .wasm and .pck, says gzip shrinks the .wasm to about a quarter, and asks for .wasm to be served as application/wasm (serving docs). itch.io’s CDN gzips uploaded .wasm and .pck files on its own (HTML5 upload docs); on your own host, switch compression on. Keep source and press-kit assets out of the export with .gdignore (import docs).
8. Check mobile input, audio and fullscreen
Add touch actions before calling the game mobile-ready. Godot’s TouchScreenButton supports simultaneous touches and an action property; map left, right and jump to the same Input Map actions used by the keyboard. Test holding a direction while jumping, releasing outside a button and rotating the device. The mobile-game guide covers that test session.
Godot documents these browser constraints; build the checks around its web export limitations:
- Audio: require a click, tap or key press at Start. Some browsers restrict autoplay. Default web Sample playback excludes audio effects and procedural generation; Stream restores the engine’s audio features with latency tradeoffs.
- Fullscreen and pointer lock (Godot calls it mouse capture): request them inside a pressed input-event callback such as
_inputor_unhandled_input. Polling the Input singleton is insufficient. - Mobile: web export can run, but native Android/iOS exports perform better. Test the real phone; Compatibility is a renderer choice, not a speed guarantee.
- Tab changes: the browser pauses processing in an inactive tab. Check resume and held-input state when returning.
Keep the first build’s sound to simple samples, and make fullscreen optional. A player should be able to finish and restart while the game stays inside its frame.
Godot games with recorded AI workflows
The catalog lists 8 Godot games as of October 1, 2026. Their entries record the engine and, where the creator named them, the AI tools; none says which MCP server, export settings or test framework it used.
- StarJam: its entry, sourced from the creator’s Vibe Jam 2026 submission, says it was built in Godot with Claude Code. It combines tower defense with clicker income.
- ironclash: also built in Godot with Claude Code, according to its Vibe Jam 2026 submission. It’s an online multiplayer battle game that runs in desktop and phone browsers.
- Pro Skater: The Warehouse: its entry cites the repository README, records Claude Opus 5.5, and says the game runs in Godot with GDScript as a web export and a Windows build. The creator says Python scripts in Blender generate every 3D asset and the sound is synthesized in code.
- Almost Surgery: its submission-sourced entry records Godot, Codex and a single-player browser game about performing operations.
- Pocket Salvage: its entry cites the project README for Claude (the chat app) and Codex, and records Godot.
- Ikuta ’73: its entry cites the itch.io game page and AI Browser Game Jam 4 rules. The creator credits Claude Code with code, sound effects, modeling and level QA, and Grok Build with title-screen and lens-flare code.
Browse the Godot hub, Claude Code games, Codex games and platformers. For character art after the loop works, use AI sprite animation.
Finish the browser build
Call this exercise done when a fresh browser can start, move, jump, collect, win and restart in the release export, both directly and in the intended iframe. Repeat with touch controls on a phone; check the console and missing network requests. Record the engine, export settings, test results and remaining limits with the build.
Then submit the game with its playable URL and an accurate account of what the AI made. GamesByAI lists playable games under its editorial policy; listing here does not host the Godot export for you.
Games to look at
StarJam
Place defenses and click for income in a tower defense idler hybrid.
ironclash
Fight multiplayer battles from tanks, helicopters, and FPV drones.
Pro Skater: The Warehouse
Skate a two-minute run through a warehouse park, chaining tricks and hitting goals.
Almost Surgery
Perform shady operations on patients with saws and shocks to earn money.
Pocket Salvage
Swing a crane, switch tools, and sort scrap into bins before time runs out.
Ikuta '73
Mow lawns in a 1973 Tokyo summer and rest in the shade to keep your stamina up
Questions
Can Claude Code make Godot games?
Yes. It edits GDScript and Godot's text scene files, and runs the Godot binary from the terminal to import, check scripts, run and export. StarJam and ironclash are Godot games whose creators credit Claude Code.
Do I need Godot MCP to use Claude Code or Codex?
No. An agent can edit GDScript and text scene files, run Godot from the terminal and read its output. MCP adds named tools such as run_project and get_debug_output. Keep the CLI checks even when you connect it.
Can Godot 4 C# games export to a browser?
Godot's current web export docs say projects written in C# using Godot 4 cannot be exported to the web. This guide uses GDScript. The docs point C# web users to Godot 3 instead.
Should I enable Thread Support for an itch.io game?
Leave it off for this small game and ordinary iframe delivery. Enabling it requires SharedArrayBuffer and cross-origin isolation. itch.io provides an opt-in SharedArrayBuffer setting, but an external embedding page also has to meet the browser's isolation and permissions requirements.
Does a clean headless run prove my Godot game works?
No. It catches startup and runtime errors on the path it executes, but doesn't check the rendered scene or play the controls. Run rule tests, inspect the visible game and play the exported browser build, including restart and touch input.