Godot Autoload singletons: architecture and trust boundaries
Use Godot Autoloads for shared state across scenes, but respect trust boundaries and failure modes before relying on them.
08 Feb 2026, 19:43 UTC

Problem and takeaway
You need data to survive scene changes in Godot, but passing references between every level and menu creates fragile, hard-to-test code.
Register one Autoload node for shared, explicitly synchronized state. Verify it persists through SceneTree.change_scene_to_file, and replace it with a scoped manager when you need per-session isolation or multiplayer authority separation.
Requirements
- Godot 4.x is assumed; Godot 3.x uses different property and node-access syntax.
- A script or scene that must outlive individual levels or menus.
- State that is genuinely global, such as player progress or audio settings, not level-specific data.
Smallest suitable design
One Autoload script holding only immutable configuration or explicitly synchronized mutable state. Access it by its registered name or through get_node("/root/Name").
In the editor, open Project > Project Settings > Autoload, set Path to res://scripts/GameState.gd, Name to GameState, and add it.
extends Node
const MAX_LIVES: int = 3
var lives: int = MAX_LIVES
func reset() -> void:
lives = MAX_LIVES
From any scene script:
func _on_damage_taken() -> void:
var state := get_node("/root/GameState")
if state == null:
push_warning("GameState is not in the tree yet")
return
state.lives -= 1
Where to run: the Project Settings editor and any script attached to a node in a scene. Required permission: write access to project.godot and the script files. Expected check: the named node appears under /root in the running Remote SceneTree. Risk: edits to project.godot may not take effect until the project restarts.
Trust and data boundaries
- Autoloads load before the main scene and run with project privileges.
- Every loaded scene can reach the node, so any exposed mutable resource is shared by default.
- An Autoload is not a security boundary. Do not store server secrets or rely on it for anti-cheat authority.
- Do not store scene-specific node references in an Autoload; they become invalid when the owning scene is freed.
Operational checks
- Add the Autoload in Project Settings and restart the project.
- While running, open the Remote SceneTree and confirm
GameStateexists under/root. - Change scenes at runtime with
get_tree().change_scene_to_file("res://scenes/Level.tscn")and confirm the node and itslivesvalue survive. - Quit and restart. Confirm state resets unless you explicitly save it to disk.
Failure modes
- Accessing the Autoload before tree entry returns null; accessing it after the node is freed returns an invalid object.
- Circular Autoload dependencies— for example, Autoload A waits for B in
_readywhile B waits for A— can block project startup. - Heavy blocking logic in
_readydelays the first frame. - Autoloads are not automatically thread-safe. Mutable state read from physics or worker threads needs a mutex or main-thread marshalling such as
call_deferred.
When to change the design
- You need per-session isolation, such as multiple matches running in the same process.
- You need multiplayer authority separation between server and client state.
- You want to unit test game logic without loading the full SceneTree.
In those cases, replace the Autoload with dependency injection or a scoped manager node instantiated under the scene that owns the session.
Rollback and limitations
Adding an Autoload changes project state. Rollback by opening Project Settings > Autoload, selecting the entry, clicking Remove, and restarting the project. Scene scripts that reference /root/GameState will fail at runtime until they are updated.
This note targets Godot 4.x. Autoload state lives in memory and resets on process restart unless you serialize it to disk. The configuration above is a design pattern, not a tested measurement; verify it in your project.
Practical check: run the project, inspect the Remote SceneTree, change scenes, confirm the Autoload and its state survive, then restart and confirm the state resets.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.