Lightweight, scene-friendly time scaling for Godot 4.5+. Define clocks, route them to timelines, and slow / speed parts of your game independently.
- Features
- Compatibility
- Installation
- Quick Start
- Core Concepts
- Tween Integration
- Customization
- Multiple named clocks with parent blending (world, player, enemy, environment by default).
- Drop-in
TimeflowTimelinenode exposes a clock's effectivetime_scaleto your scripts. - Global
Timeflowautoload to fetch or edit clocks from anywhere. - Works with 2D or 3D; deterministic blending (additive or multiplicative).
Godot 4.2+.
- Download the latest release: https://github.com/zekostudio/godot-timeflow/releases
- Copy
addons/timeflowinto your project'saddonsfolder. - In the editor: Project > Project Settings > Plugins and enable Timeflow.
- The plugin registers
res://addons/timeflow/timeflow.tscnas an autoload namedTimeflow. - It contains four clocks:
WORLD(root),PLAYER,ENEMY,ENVIRONMENT(children ofWORLD).
-
Add a TimeflowTimeline to a scene
- Add a
TimeflowTimelinenode. - Assign a
TimeflowClockConfigresource toclock_configuration(for exampleplayer_clock.tresfor the player).
- Add a
-
Consume the time scale in code
extends CharacterBody2D const TimeflowTimeline = preload("res://addons/timeflow/scripts/clock/timeflow_timeline.gd") const SPEED: float = 300.0 @export var timeline: TimeflowTimeline func _physics_process(delta: float) -> void: var input: Vector2 = Vector2( Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left"), Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up") ) velocity = input.normalized() * SPEED * timeline.time_scale move_and_slide()
-
Change time from anywhere
const TimeflowClockConfig = preload("res://addons/timeflow/scripts/clock/timeflow_clock_config.gd") @export var clock_configuration: TimeflowClockConfig func _process(_delta: float) -> void: Timeflow.get_clock(clock_configuration).local_time_scale = 0.5 # or by key # Timeflow.get_clock_by_key("PLAYER").local_time_scale = 0.5
Defines a clock identity, defaults, and parent relationship.
Properties:
key: String— Identifier used to fetch the clock.default_local_time_scale: float— Default local scale applied at clock initialization.default_paused: bool— Default paused state.parent_key: String— Optional parent clock key (""means no parent).parent_blend_mode: TimeflowBlendMode—MULTIPLICATIVE(default) orADDITIVE.min_time_scale: float/max_time_scale: float— Clamp range for runtime local scale changes.
Computes an independent time_scale, optionally blended with a parent clock.
Properties:
configuration: TimeflowClockConfig— Which clock this node represents.local_time_scale: float— Runtime local multiplier (initialized from config).paused: bool— Runtime paused state (initialized from config).parent_blend_mode: TimeflowBlendMode— Runtime blend mode (initialized from config).- Runtime behavior: changing
local_time_scaleaffects this clock and all clocks that depend on it through the parent hierarchy.
Method:
get_time_scale() -> float— Returns the blended time scale.
Bridge node that exposes the effective time scale of a chosen clock.
Properties:
time_scale: float— The resolved time scale of the targeted clock.mode: TimeflowTimelineMode—GLOBAL(default);LOCAL.clock_configuration: TimeflowClockConfig— Used when mode isGLOBAL.local_clock: TimeflowClock— Used when mode isLOCAL.
Registry for all clocks; available globally.
Methods:
has_clock(configuration: TimeflowClockConfig) -> boolget_clock(configuration: TimeflowClockConfig) -> TimeflowClockget_clock_by_key(key: String) -> TimeflowClockadd_clock(configuration: TimeflowClockConfig) -> TimeflowClockremove_clock(configuration: TimeflowClockConfig) -> void
Drop these glue scripts next to existing nodes to keep their playback in sync with a TimeflowTimeline without rewriting their logic.
scripts/adapters/sync/timeflow_animation_player_sync.gd— DrivesAnimationPlayer.speed_scalefrom a boundTimeflowTimeline.scripts/adapters/sync/timeflow_gpu_particles_2d_sync.gd/timeflow_gpu_particles_3d_sync.gd— Drive particlespeed_scalein 2D or 3D.scripts/adapters/areas/timeflow_area_2d.gd/timeflow_area_3d.gd— Appliestimescale_multiplierto bodies entering anArea2DorArea3D.
Area timeline integration fallback order (first match wins):
- Method:
set_area_timescale_multiplier(multiplier: float) - Property:
area_timescale_multiplier: float
Minimal body integration (recommended):
var area_timescale_multiplier: float = 1.0
func _physics_process(_delta: float) -> void:
velocity = input_dir * speed * timeline.time_scale * area_timescale_multiplier
move_and_slide()Usage (example for 2D particles):
- Add a plain
Nodeas a sibling or parent, attachtimeflow_gpu_particles_2d_sync.gd. - Assign
gpu_particles_2dto your particles node andtimelineto the relevantTimeflowTimeline(e.g., player or enemy). - Press play — particle playback automatically speeds up / slows down with the clock.
This plugin includes a rewind recorder based on timeline direction:
scripts/rewind/timeflow_recorder.gd(TimeflowRecorder) — records snapshots and applies them while time is reversed.scripts/rewind/timeflow_rewindable.gd(TimeflowRewindable) — base rewind contract for all rewindable adapters.scripts/rewind/timeflow_rewindable_2d.gd(TimeflowRewindable2D) — captures/restores aNode2Dtransform.scripts/rewind/timeflow_rewindable_path_follow_2d.gd(TimeflowRewindablePathFollow2D) — captures/restoresPathFollow2D.progress.
Quick setup:
- Add a rewindable adapter node (
TimeflowRewindable2DorTimeflowRewindablePathFollow2D) and assign its target. - Add
TimeflowRecorderto the same scene and assign:timelineto yourTimeflowTimelinerewindablesto one or moreTimeflowRewindablenodes
- Set the clock scale negative (for example
Timeflow.get_clock_by_key("PLAYER").local_time_scale = -1.0) to rewind. - Return the clock to positive to resume forward simulation and recording.
Recorder tuning:
recording_durationcontrols max rewind window (seconds).recording_intervalcontrols snapshot frequency (higher frequency = smoother rewind, more memory).record_when_pausedcontrols whether snapshots are captured whiletime_scale == 0.TimeflowRewindable2D.disable_target_processing_while_rewindingprevents gameplay scripts on that node from fighting restored rewind states.- When rewind stops (or reaches the recorded history limit),
TimeflowRecorderclears history and starts recording from the current state again. TimeflowRewindablePathFollow2D.snap_on_discontinuityanddiscontinuity_ratioreduce jitter from large progress jumps (for example loop/reset boundaries).
Set the speed_scale property to keep a Tween synchronized with a TimeflowTimeline.
extends Node2D
const TimeflowTimeline = preload("res://addons/timeflow/scripts/clock/timeflow_timeline.gd")
@export var timeline: TimeflowTimeline
@export var mover: Node2D
@export var duration: float = 3.0
var _tween: Tween
func _ready() -> void:
_tween = get_tree().create_tween()
_tween.tween_property(mover, "position:x", 500.0, duration)
func _process(_delta: float) -> void:
if _tween != null:
_tween.set_speed_scale(timeline.time_scale)Swap the autoload scene
- Duplicate
res://addons/timeflow/timeflow.tscnto another path. - Edit the copy (add/remove global clocks, change hierarchy).
- In Project > Project Settings > Addons > Timeflow, set
autoload_pathto your scene (e.g.,res://scenes/timeflow.tscn). - Disable and re-enable the plugin (or restart the editor) to reload.
Open the demo scene res://addons/timeflow/demo/demo.tscn for a runnable example.