Saltmire
Practical Godot 4 tutorials — game feel, saving, and shipping polish fast.

5 Godot 4 scene transition effects (and how to build each one)

Swapping scenes in Godot 4 is one line: get_tree().change_scene_to_file(). Making that swap feel intentional is the part that takes effort. Here are five transition effects — from dead simple to shader-powered — with paste-able GDScript for each.

1. Fade to black

The classic. A ColorRect at CanvasLayer 100 sits invisible on top of everything. Tween its alpha to 1, change the scene, tween back to 0:

# SceneTransition.gd — autoload
@onready var overlay: ColorRect = $CanvasLayer/Overlay  # full-viewport, black, alpha 0

func fade_to(path: String, duration := 0.3) -> void:
    var t := create_tween()
    t.tween_property(overlay, "modulate:a", 1.0, duration)
    await t.finished
    get_tree().change_scene_to_file(path)
    t = create_tween()
    t.tween_property(overlay, "modulate:a", 0.0, duration)

Call it: SceneTransition.fade_to("res://scenes/Level2.tscn"). Squaring the tween curve (Tween.TRANS_SINE) makes the fade-out feel less abrupt.

2. Iris wipe (circle close)

An iris wipe closes a circle over the screen, swaps the scene, then opens again. You need one shader on the ColorRect:

// iris.gdshader
shader_type canvas_item;
uniform float radius : hint_range(0.0, 1.5) = 1.5;
uniform vec2  center = vec2(0.5, 0.5);

void fragment() {
    float dist = length(UV - center);
    COLOR = vec4(0.0, 0.0, 0.0, step(radius, dist));
}

Then in GDScript:

func iris_to(path: String, duration := 0.4) -> void:
    var mat: ShaderMaterial = overlay.material
    var t := create_tween()
    t.tween_method(func(v): mat.set_shader_parameter("radius", v), 1.5, 0.0, duration)
    await t.finished
    get_tree().change_scene_to_file(path)
    t = create_tween()
    t.tween_method(func(v): mat.set_shader_parameter("radius", v), 0.0, 1.5, duration)

Point center at a character or door for a dramatic theatrical close.

3. Pixelate

The screen dissolves into chunky blocks before the cut. Capture the screen with hint_screen_texture and floor each UV to the nearest block:

// pixelate.gdshader
shader_type canvas_item;
uniform float block_size : hint_range(1.0, 128.0) = 1.0;
uniform sampler2D screen_texture : hint_screen_texture, filter_nearest;

void fragment() {
    vec2 vp = 1.0 / SCREEN_PIXEL_SIZE;
    vec2 snapped = floor(SCREEN_UV * vp / block_size) * block_size / vp;
    COLOR = texture(screen_texture, snapped);
}

Tween block_size from 1.0 up to 64.0 (fully pixelated), change scene, tween back down. Looks great for retro or lo-fi aesthetics.

4. Slide / pan

No shader needed — just move nodes. Instantiate the next scene offscreen, then tween both the current scene out and the new one in simultaneously:

func slide_to(path: String, dir := Vector2.LEFT, duration := 0.4) -> void:
    var vp := get_viewport().get_visible_rect().size
    var next := load(path).instantiate()
    next.position = -dir * vp
    get_tree().root.add_child(next)

    var t := create_tween().set_parallel()
    t.tween_property(get_tree().current_scene, "position", dir * vp, duration)
    t.tween_property(next, "position", Vector2.ZERO, duration)
    await t.finished

    get_tree().current_scene.queue_free()
    get_tree().current_scene = next

Pass Vector2.RIGHT / Vector2.UP / Vector2.DOWN to control slide direction. Add .set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_CUBIC) for a polished mobile-menu feel.

5. Dissolve (noise dither)

A dissolve removes pixels in a pseudo-random order using a grayscale noise texture as a threshold map — much more interesting than a uniform fade:

// dissolve.gdshader
shader_type canvas_item;
uniform float threshold : hint_range(0.0, 1.0) = 0.0;
uniform sampler2D noise : hint_default_white;
uniform sampler2D screen_texture : hint_screen_texture, filter_nearest;

void fragment() {
    float n = texture(noise, UV).r;
    if (n < threshold) { discard; }
    COLOR = texture(screen_texture, SCREEN_UV);
}

Assign any seamless noise texture to noise, then tween threshold from 0.0 to 1.0 to dissolve out. Use a SubViewport to capture the outgoing scene so the dissolve continues to run after the scene has already changed.

Which one to use

Effect Complexity Best for
Fade Trivial Any genre — never wrong
Iris One shader Dramatic story beats, cutscenes
Pixelate One shader Retro, lo-fi, pixel-art games
Slide Pure GDScript Menu flows, mobile feel
Dissolve Shader + noise texture Cinematic, horror, RPG

Skip the wiring

Each effect above is ~15 lines of code. The time cost is the autoload setup, managing which CanvasLayer owns what, and making the await-able API safe against overlapping calls. If you want all five (plus wipes and a smooth fade-in-on-load default) as a single drop-in for Godot 4, that is what Saltmire Transitions is.

Built this the long way once too many times. Saltmire Transitions does it as a drop-in tool: https://saltmire.itch.io/saltmire-transitions-scene-transition-kit-for-godot-4