Simple Scene Switcher in Godot 🕹️ cover image.

Simple Scene Switcher in Godot 🕹️

edo · Fri Oct 24 2025

Introduction

Scene management is a fundamental aspect of game development. Whether you’re creating a menu system, transitioning between levels, or building a complex game world, you need a reliable way to switch between scenes. In this tutorial, we’ll create a simple yet effective scene switcher for Godot.

Here you can see an example of what we will achieve:

Prerequisites

Before we begin, make sure you have:

  • Godot 4.x installed (this tutorial uses Godot 4, but can be adapted for Godot 3)
  • Basic understanding of GDScript
  • Familiarity with Godot’s node system

What We’ll Build

We’ll create a SceneSwitcher singleton (autoload) that handles:

  • Loading scenes by path
  • Smooth transitions between scenes
  • Optional fade effects
  • Error handling

Step 1: Creating the SceneSwitcher Script

First, let’s create our main SceneSwitcher script.

For better organization, we will create the script in a new folder called autoloads inside the res:// directory.

# scene_switcher.gd
extends Node


func switch_to(p_scene_path: String) -> void:
    assert(FileAccess.file_exists(p_scene_path), "Scene path does not exist: %s" % p_scene_path)

    var error: Error = get_tree().change_scene_to_file(p_scene_path)
    assert(error == OK, "Failed to switch scene to: %s" % p_scene_path)

This is the basic structure of our SceneSwitcher. The switch_to function takes a scene path as an argument, checks if the file exists, and then changes the scene using Godot’s change_scene_to_file method. It also handles errors gracefully. However, this is just the starting point. We will enhance it with transitions in the next steps.

Step 2: Setting Up the Autoload

Now we need to register our SceneSwitcher as an autoload singleton. For that, follow these steps:

  1. In the Godot editor, go to Project -> Project Settings.
  2. Navigate to the AutoLoad tab.
  3. Click on the Add button.
  4. Select the autoloads/scene_switcher.gd script you created earlier.
  5. Set the Node Name to SceneSwitcher.
  6. Click Add.

Autoload Setup

Step 3: Creating Transition Effects

Let’s add some polish with fade transitions. To do this, we’ll create a simple fade effect using a ColorRect node.

We can create the necessary nodes by code:

# scene_switcher.gd
extends Node

var canvas_layer: CanvasLayer
var fade_rect: ColorRect
var fade_duration: float = 0.5
var transition_delay: float = 1.0


func _ready() -> void:
    canvas_layer = CanvasLayer.new()
    canvas_layer.layer = 100  # Ensure it's on top

    add_child(canvas_layer)

    fade_rect = ColorRect.new()
    fade_rect.color = Color(0, 0, 0, 1)

    # Set anchors to cover the entire screen
    fade_rect.anchor_left = 0
    fade_rect.anchor_top = 0
    fade_rect.anchor_right = 1
    fade_rect.anchor_bottom = 1

    # Ignore mouse events
    fade_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE

    canvas_layer.add_child(fade_rect)

    await get_tree().create_timer(transition_delay).timeout
    
    # Initial fade out
    get_tree().create_tween().tween_property(fade_rect, "modulate:a", 0, fade_duration)


func switch_to(p_scene_path: String) -> void:
	assert(FileAccess.file_exists(p_scene_path), "Scene path does not exist: %s" % p_scene_path)

	var error: Error = get_tree().change_scene_to_file(p_scene_path)
	assert(error == OK, "Failed to switch scene to: %s" % p_scene_path)

At this point, we have a basic fade effect that fades in when the game starts. Next, we need to implement the fade-out effect when switching scenes.

# -- REST OF THE CODE

func switch_to(p_scene_path: String) -> void:
	assert(FileAccess.file_exists(p_scene_path), "Scene path does not exist: %s" % p_scene_path)

    # Fade In
    await get_tree().create_tween().tween_property(fade_rect, "modulate:a", 1.0, fade_duration).finished

	var error: Error = get_tree().change_scene_to_file(p_scene_path)
	assert(error == OK, "Failed to switch scene to: %s" % p_scene_path)

    await get_tree().create_timer(transition_delay).timeout

    # Fade Out
    get_tree().create_tween().tween_property(fade_rect, "modulate:a", 0, fade_duration)

Step 4: Using the SceneSwitcher

Now that we have our SceneSwitcher set up, we can use it to switch scenes from anywhere in our game since it’s an autoload singleton which means we can access it globally using SceneSwitcher object.

Testing the Scene Switcher

We will setup a simple use case, we will create two scenes: FirstScene.tscn and SecondScene.tscn. Each scene will have a button that, when pressed, will switch to the other scene using our SceneSwitcher.

Here you can see the first scene setup:

First Scene

And here is the second scene setup:

Second Scene

The code for the button in the FirstScene would look like this (you need to attach this script to the scene root):

# first_scene.gd
extends Node2D

func _on_button_pressed() -> void:
	SceneSwitcher.switch_to("res://scenes/second_scene/second_scene.tscn")

And the code for the button in SecondScene would look like this (you need to attach this script to the scene root):

# second_scene.gd
extends Node2D

func _on_button_pressed() -> void:
	SceneSwitcher.switch_to("res://scenes/first_scene/first_scene.tscn")
Make sure to connect the button's `pressed` signal to the `_on_button_pressed` function in both scenes.

With this setup, you can now switch between the two scenes with a smooth fade transition just as we see in the video above.

Additional Features

Finally, we will add one more feature: Emit a signal when fade transition finish. This could be useful for triggering events after a scene has fully loaded. So, here is the full code updated:

# scene_switcher.gd
extends Node

signal finished

var canvas_layer: CanvasLayer
var fade_rect: ColorRect
var fade_duration: float = 0.5
var transition_delay: float = 1.0


func _ready() -> void:
	canvas_layer = CanvasLayer.new()
	canvas_layer.layer = 100  # Ensure it's on top
	
	add_child(canvas_layer)

	fade_rect = ColorRect.new()
	fade_rect.color = Color(0, 0, 0, 1)

	# Set anchors to cover the entire screen
	fade_rect.anchor_left = 0
	fade_rect.anchor_top = 0
	fade_rect.anchor_right = 1
	fade_rect.anchor_bottom = 1

	# Ignore mouse events
	fade_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE

	canvas_layer.add_child(fade_rect)

	await get_tree().create_timer(transition_delay).timeout

	# Initial fade out
	await get_tree().create_tween().tween_property(fade_rect, "modulate:a", 0, fade_duration).finished
	finished.emit()


func switch_to(p_scene_path: String) -> void:
	assert(FileAccess.file_exists(p_scene_path), "Scene path does not exist: %s" % p_scene_path)

	# Fade In
	await get_tree().create_tween().tween_property(fade_rect, "modulate:a", 1.0, fade_duration).finished

	var error: Error = get_tree().change_scene_to_file(p_scene_path)
	assert(error == OK, "Failed to switch scene to: %s" % p_scene_path)

	await get_tree().create_timer(transition_delay).timeout

	# Fade Out
	get_tree().create_tween().tween_property(fade_rect, "modulate:a", 0, fade_duration)
	finished.emit()

Hipothetically, you can now connect to the finished signal from any scene to perform actions after the transition is complete:

# any_scene.gd
extends Node2D

func _ready() -> void:
    SceneSwitcher.finished.connect(_on_scene_switch_finished)


func _on_scene_switch_finished() -> void:
    print("Scene switch completed!")

Conclusion

You now have a functional scene switcher that you can use in any Godot project! This foundation can be extended with additional features as your game grows in complexity.

SceneSwitcher Addon

Also we have prepared a plug-and-play godot addon with the complete SceneSwitcher implementation, which you can download from our GitHub repository: SceneSwitcher Addon. This addon will be available in the Godot Asset Library soon.

Resources


Happy game development! 🎮

edo

edo

CEO at Pandita Studio. Senior software and game developer (C++, Python, Java, web, Godot). Digital illustrator and university lecturer.

© Pandita Videogames Studio S.A.S. de C.V.
contact@panditastudio.com
SITE
HOME
GAMES
SERVICES
BLOG
STUDIO
CONTACT
INTERNSHIPS
PRESS
EXTERNAL
DISCORD
FACEBOOK
INSTAGRAM
YOUTUBE
X