GDSense how-to guide

Godot Autoloads and Singletons: When and How to Use Them

Register a Godot 4 autoload, build a typed game-state singleton and an event bus, and avoid the lifecycle mistakes that make global state hard to debug.

Published Updated

All documentation guides

The short answer

An autoload is a scene or script that Godot adds to the scene tree before your main scene and keeps alive while the game runs. Register one under Project → Project Settings → Globals → Autoload, and with Global Variable enabled you can reach it by name from any script. Use autoloads for state and services that truly span scenes, such as game progress, audio, or an event bus. Keep level-specific data in its level, and reset global state deliberately when a new game starts.

What is an autoload in Godot?

Godot instantiates every autoload at startup and adds it as a child of the root viewport, ahead of the main scene. It is an ordinary Node, so it runs _ready(), _process(), and signals like any other node, and it survives calls such as change_scene_to_file() because it is not part of the current scene.

To register one, open Project → Project Settings → Globals → Autoload, choose a .gd or .tscn file, give it a name in PascalCase, and keep Global Variable enabled. That name becomes a global identifier, so GameState.coins works in any script. Autoloads load in the order listed, top to bottom.

  1. 1

    Create the script

    Save res://autoload/game_state.gd extending Node.

  2. 2

    Register it

    Add it in Globals → Autoload with the name GameState and Global Variable enabled.

  3. 3

    Use it anywhere

    Read and call GameState from any script, and connect to its signals.

Godot singletons (autoload) documentation

When should I use an autoload, and when should I not?

An autoload is the right tool when data or a service must outlive scene changes and be reachable from many unrelated places. It is the wrong tool when it only saves you from passing a reference, because hidden global access makes it harder to see which code changes what.

Autoload decision guide
NeedAutoload?Better alternative when not
Score, coins, or unlocks that persist across levelsYes—
Music that keeps playing through scene changesYes—
Game-wide events such as player_died or level_completedYes, as an event bus—
Data used only by one levelNoKeep it on the level’s root node
Finding the player from an enemyNoUse a group, an exported reference, or a signal
Pure helper functionsNoUse static functions in a class_name script

How do I build a typed game-state autoload?

Keep the state typed, change it through methods, and announce changes with signals. Listeners then update themselves, and nothing outside the autoload writes its variables directly. The reset() method gives a new game a known starting point, because the autoload itself is never recreated between scenes.

res://autoload/game_state.gd — register as GameState
extends Node

signal coins_changed(coins: int)
signal level_unlocked(level_id: StringName)

var coins: int = 0
var unlocked_levels: Array[StringName] = [&"level_1"]

func add_coins(amount: int) -> void:
    if amount <= 0:
        return
    coins += amount
    coins_changed.emit(coins)

func unlock_level(level_id: StringName) -> void:
    if level_id in unlocked_levels:
        return
    unlocked_levels.append(level_id)
    level_unlocked.emit(level_id)

func reset() -> void:
    coins = 0
    unlocked_levels = [&"level_1"]
    coins_changed.emit(coins)
res://ui/coin_label.gd — attach to a Label
extends Label

func _ready() -> void:
    GameState.coins_changed.connect(_on_coins_changed)
    _on_coins_changed(GameState.coins)

func _on_coins_changed(coins: int) -> void:
    text = "Coins: %d" % coins

Keep in mind: The label renders the current value once in _ready(). A signal only reaches listeners that are already connected, so a HUD created mid-game would otherwise stay blank until the next change.

How do I build an event bus with an autoload?

An event bus is an autoload that only declares signals. Any node can emit a game-wide event without knowing who listens, and any node can listen without holding a reference to the emitter. Keep the list short and name events in the past tense, so each one reads as something that already happened.

Godot may flag bus signals as unused because the bus never emits them itself. That is expected for this pattern, and the @warning_ignore annotation silences it per signal.

res://autoload/events.gd — register as Events
extends Node

@warning_ignore("unused_signal")
signal player_died

@warning_ignore("unused_signal")
signal level_completed(level_id: StringName, time_seconds: float)
res://levels/level_exit.gd — attach to an Area2D
extends Area2D

@export var level_id: StringName = &"level_1"

var _start_msec: int = 0

func _ready() -> void:
    _start_msec = Time.get_ticks_msec()
    body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
    if not body.is_in_group(&"player"):
        return
    var elapsed: float = (Time.get_ticks_msec() - _start_msec) / 1000.0
    Events.level_completed.emit(level_id, elapsed)
Godot signals introduction

What are the common autoload mistakes?

  • Give the autoload a different name from any class_name in the project; Godot reports an error when a class name hides an autoload singleton.
  • Reset global state explicitly when a new game starts; changing scenes does not recreate autoloads.
  • Do not keep references to nodes from a level after it is freed; check is_instance_valid() or clear the reference on exit.
  • Order autoloads so each one is listed after anything it uses in _ready().
  • Disconnect or let freed listeners disconnect naturally; avoid connecting the same callback twice when a scene reloads.
  • Running a single scene with F6 still loads every autoload, so a test scene sees the same global state as the full game.

Keep in mind: When global state behaves unexpectedly, open the Remote scene tree while the game runs. Autoloads appear under root beside the current scene, which makes it easy to inspect their live values.

How can GDSense help with autoloads?

GDSense includes your autoload names and script paths in the project map it attaches to conversations, so answers call GameState or Events by their real names instead of inventing a manager that does not exist. Ask it to trace who reads and writes a piece of global state before you change it.

Autoload review request
@file res://autoload/game_state.gd
Coins sometimes carry over after I choose New Game from the main menu.
Trace where coins are written and where reset() should be called.
Keep the coins_changed signal and the GameState name. Suggest the smallest fix and a test.

Try this workflow inside Godot

Use GDSense to ask questions, attach the context you choose, and review proposed changes without leaving the editor.