# How Godot SceneTree Initialization and the Main Loop Work: A Deep Dive into the Engine Source

> Explore Godot's SceneTree initialization and main loop. Learn how Main::start() drives physics_process() and process() for seamless engine updates.

- Repository: [Godot Engine/godot](https://github.com/godotengine/godot)
- Tags: deep-dive
- Published: 2026-02-26

---

**Godot's SceneTree initialization begins in `Main::start()` which creates a `MainLoop` instance (typically `SceneTree`) and registers it with the OS singleton; the platform-specific OS run-loop then drives the engine by calling `physics_process()` for fixed-step physics and `process()` for frame updates until the application quits.**

The `SceneTree` serves as the central nervous system of every Godot application, managing the node hierarchy,viewport rendering, and the execution flow that powers both gameplay and editor functionality. Understanding how the godotengine/godot repository initializes this system and maintains its main loop is essential for engine contributors and advanced users building custom runtime behavior. This article examines the exact source code paths that bootstrap the SceneTree and the architectural patterns that govern the per-frame execution cycle.

## The Startup Sequence: From Main to SceneTree

The engine boot process follows a strict three-phase initialization defined in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/main.cpp). This sequence establishes the foundation upon which the SceneTree is constructed and activated.

### Entry Points in main/main.cpp

Execution begins with static methods in the `Main` class. First, `Main::setup()` parses command-line arguments and initializes low-level subsystems. Next, `Main::setup2()` completes resource loading and server initialization. Finally, `Main::start()` determines which `MainLoop` implementation to instantiate based on the execution context (editor, project manager, or standalone game).

During `Main::start()`, the engine evaluates whether to launch the editor or runtime:

```cpp
// main/main.cpp (simplified)
MainLoop *main_loop = nullptr;
if (editor) {                     // --editor flag or project manager
    main_loop = memnew(SceneTree);   // default scene-tree implementation
}
...
OS::get_singleton()->set_main_loop(main_loop);   // registers it with OS

```

The `memnew(SceneTree)` allocation creates the concrete `MainLoop` object, while `OS::set_main_loop()` stores this pointer within the OS singleton for later retrieval by the platform layer.

### Registering with the OS Layer

The **OS abstraction** ([`core/os/os.h`](https://github.com/godotengine/godot/blob/main/core/os/os.h)) acts as the bridge between platform-specific code and engine logic. When `set_main_loop()` receives the SceneTree pointer, it prepares the system to delegate lifecycle events to this object. This registration must complete before `OS::run()` begins, as the run-loop assumes a valid `MainLoop` instance exists.

## The MainLoop Interface and OS Abstraction

Godot decouples the platform run-loop from game logic through the abstract `MainLoop` class defined in [`core/os/main_loop.h`](https://github.com/godotengine/godot/blob/main/core/os/main_loop.h). This interface declares the virtual methods that every main loop implementation—including SceneTree—must override.

### The MainLoop Virtual Interface

The `MainLoop` base class establishes four critical lifecycle hooks:

```cpp
class MainLoop : public Object {
    GDCLASS(MainLoop, Object);
protected:
    static void _bind_methods();

    GDVIRTUAL0(_initialize)
    GDVIRTUAL1R(bool, _physics_process, double)   // physics tick
    GDVIRTUAL1R(bool, _process, double)          // per-frame tick
    GDVIRTUAL0(_finalize)

public:
    virtual void initialize();       // called once at startup
    virtual bool physics_process(double p_time);  // fixed timestep
    virtual bool process(double p_time);          // variable frame time
    virtual void finalize();        // called on shutdown
    virtual ~MainLoop() {}
};

```

The boolean return values from `physics_process()` and `process()` serve as exit signals; returning `true` requests immediate engine termination.

### The OS Run-Loop Implementation

Platform-specific OS classes (such as `OS_Unix` or `OS_Windows`) implement the `run()` method declared in [`core/os/os.h`](https://github.com/godotengine/godot/blob/main/core/os/os.h). This method contains the actual while-loop that drives the engine until `_quit` becomes true:

```cpp
// core/os/os.cpp (conceptual)
int OS::run() {
    while (!_quit) {
        // handle OS events (window messages, input, etc.)
        
        if (main_loop) {
            // fixed-step physics
            if (main_loop->physics_process(delta)) break;
            
            // idle processing and rendering
            if (main_loop->process(delta)) break;
        }
    }
    return exit_code;
}

```

This architecture ensures that **physics processing** executes at a fixed timestep independent of frame rate, while **process ticks** occur as frequently as the display refresh allows.

## SceneTree Implementation Details

`SceneTree` ([`scene/main/scene_tree.h`](https://github.com/godotengine/godot/blob/main/scene/main/scene_tree.h) and [`scene/main/scene_tree.cpp`](https://github.com/godotengine/godot/blob/main/scene/main/scene_tree.cpp)) inherits from `MainLoop` and provides the concrete implementation used by default in Godot projects. It extends the basic interface with node management, group processing, and signal emission.

### Initialization Phase

When `OS::run()` first invokes the main loop, it calls `SceneTree::initialize()`. This method performs several critical setup operations:

- Configures the root **Viewport** and **Window** nodes
- Registers the tree with the `Engine` singleton
- Establishes default input handling and rendering contexts
- Emits the `"tree_changed"` signal to notify listeners that the hierarchy is ready

This initialization occurs exactly once per application lifetime, before any physics or rendering occurs.

### Physics and Process Ticks

The SceneTree implements the dual-update strategy required for stable game simulation:

**`physics_process(double p_time)`** executes at a fixed physics tick rate (default 60 Hz). It processes:
- Physics server synchronization
- Timer and tween updates tied to physics
- Node groups registered for physics callbacks
- Emission of the `"physics_frame"` signal

**`process(double p_time)`** runs once per rendered frame. It handles:
- Idle-time node group processing
- Input event propagation
- Rendering server updates and draw calls
- Emission of the `"process_frame"` signal

Both methods return `false` by default to continue execution, or `true` to trigger immediate shutdown.

### Frame Lifecycle Hooks

Between the physics and process phases, SceneTree utilizes `iteration_prepare()` and `iteration_end()` (declared in [`scene/main/scene_tree.h`](https://github.com/godotengine/godot/blob/main/scene/main/scene_tree.h)) to manage interpolation states and synchronization points. These hooks enable features like physics interpolation and ensure the rendering server receives consistent data during multi-threaded scenarios.

## Practical Code Examples

### Creating a Custom MainLoop in C++

You can bypass SceneTree entirely by implementing a custom `MainLoop` subclass:

```cpp
class MyLoop : public MainLoop {
    GDCLASS(MyLoop, MainLoop);
public:
    void initialize() override {
        print_line("MyLoop init");
    }
    
    bool physics_process(double delta) override {
        // Fixed-step physics code here
        return false; // return true to request quit
    }
    
    bool process(double delta) override {
        // Per-frame rendering logic here
        return false;
    }
    
    void finalize() override {
        print_line("MyLoop cleanup");
    }
};

// Registration:
OS::get_singleton()->set_main_loop(memnew(MyLoop));

```

### Accessing the SceneTree in GDScript

From any node in the hierarchy, access the active SceneTree using:

```gdscript
var tree = get_tree()                # Returns the current SceneTree

print(tree.get_frame())              # Current frame counter

tree.quit()                          # Request engine shutdown

```

### Connecting to Engine Signals

Monitor structural changes and frame events:

```gdscript
func _ready():
    get_tree().connect("tree_changed", self, "_on_tree_changed")
    get_tree().connect("physics_frame", self, "_on_physics_frame")

func _on_tree_changed():
    print("SceneTree structure updated")
    
func _on_physics_frame():
    print("Physics tick occurred")

```

## Summary

- **Initialization Flow**: Godot bootstraps through `Main::setup()` → `Main::setup2()` → `Main::start()`, which instantiates `SceneTree` and registers it via `OS::set_main_loop()` in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/main.cpp).
- **Abstraction Layer**: The `MainLoop` interface in [`core/os/main_loop.h`](https://github.com/godotengine/godot/blob/main/core/os/main_loop.h) decouples platform code from game logic, declaring `initialize()`, `physics_process()`, `process()`, and `finalize()` hooks.
- **Execution Loop**: Platform-specific OS implementations call the virtual methods in a tight loop, separating fixed-timestep physics from variable-time rendering.
- **SceneTree Extensions**: [`scene/main/scene_tree.cpp`](https://github.com/godotengine/godot/blob/main/scene/main/scene_tree.cpp) adds node management, group processing, and viewport rendering atop the base interface.
- **Customization**: Developers can replace the default SceneTree with custom `MainLoop` implementations by subclassing and registering with the OS singleton.

## Frequently Asked Questions

### What is the difference between MainLoop and SceneTree?

**`MainLoop`** is an abstract interface defined in [`core/os/main_loop.h`](https://github.com/godotengine/godot/blob/main/core/os/main_loop.h) that specifies the minimum contract required to drive the engine—initialization, physics processing, frame processing, and finalization. **`SceneTree`** is a concrete implementation located in [`scene/main/scene_tree.h`](https://github.com/godotengine/godot/blob/main/scene/main/scene_tree.h) that inherits from `MainLoop` and adds node hierarchy management, viewport handling, group processing, and GDScript signal emission. While all Godot games use a `MainLoop`, most use the `SceneTree` specialization unless implementing a custom runtime that does not require nodes.

### How do I access the SceneTree from a Node?

Call **`get_tree()`** from any class inheriting from `Node`. This method returns a pointer to the active `SceneTree` instance registered with the OS singleton. From GDScript, this provides access to methods like `change_scene_to_file()`, `create_timer()`, and `quit()`. In C++ module development, you can also retrieve it via `SceneTree::get_singleton()` after initialization completes.

### Can I replace the default SceneTree with a custom MainLoop?

Yes. Create a C++ class inheriting from `MainLoop`, override the virtual methods (`initialize()`, `physics_process()`, `process()`, `finalize()`), and register it with `OS::get_singleton()->set_main_loop()` before `OS::run()` begins. This technique is used for specialized applications like headless servers, custom rendering engines, or non-game tools built on Godot's core infrastructure that do not require the node system.

### How does the engine handle physics vs. process timing?

The **`physics_process()`** method executes at a fixed timestep defined by `ProjectSettings.physics/common/physics_ticks_per_second` (default 60 Hz), ensuring deterministic simulation regardless of frame rate. The **`process()`** method executes once per rendered frame, passing the actual delta time since the previous frame. The OS run-loop in [`core/os/os.cpp`](https://github.com/godotengine/godot/blob/main/core/os/os.cpp) calls both methods sequentially each iteration, allowing SceneTree to update physics servers before processing input and rendering.