~3 min read

GDExtension — native plugins in C++ and Rust

godot-cpp, gdext (Rust), when native extensions are needed and how they work.

GDScript is good for most gameplay logic, but sometimes you need to go lower:

  • A voxel engine with millions of blocks
  • Custom real-time shading with complex CPU math
  • Integration with an existing C/C++ library (physics, NN inference, audio DSP)
  • A performance hot path where even statically-typed GDScript isn’t enough

GDExtension is the modern way to extend Godot at the native level. It replaced GDNative as of Godot 4.0.

Core ideas

  • You write code in C++ (via godot-cpp) or Rust (via gdext).
  • You compile it into a dynamic library (.so / .dll / .dylib).
  • You create a .gdextension manifest that Godot loads at startup.
  • In the editor your classes appear as ordinary nodes — they are added to the scene, have properties in the Inspector, and can connect signals.

The main advantage of GDExtension over modules (which live in the Godot monorepo): you don’t have to rebuild the editor itself. Change the plugin → reload Godot → the new version works.

Web

Node native modules (.node files via node-gyp). Or WASM modules compiled from Rust/C++ for the browser.

Unity

Structure of a GDExtension plugin

my_plugin/
├── my_plugin.gdextension          ← manifest
├── src/
│   ├── register_types.cpp         ← class registration
│   ├── my_node.cpp
│   └── my_node.h
├── bin/                           ← compiled .so/.dll
│   ├── libmyplugin.linux.x86_64.so
│   ├── libmyplugin.windows.x86_64.dll
│   └── libmyplugin.macos.universal.dylib
├── SConstruct                     ← build via SCons
└── godot-cpp/                     ← submodule with bindings

my_plugin.gdextension:

[configuration]
entry_symbol = "myplugin_library_init"
compatibility_minimum = "4.4"

[libraries]
linux.x86_64 = "bin/libmyplugin.linux.x86_64.so"
windows.x86_64 = "bin/libmyplugin.windows.x86_64.dll"
macos = "bin/libmyplugin.macos.universal.dylib"

Minimal C++ example

my_node.h:

#ifndef MY_NODE_H
#define MY_NODE_H

#include <godot_cpp/classes/node3d.hpp>

namespace godot {

class MyNode : public Node3D {
    GDCLASS(MyNode, Node3D)

private:
    double speed = 1.0;

protected:
    static void _bind_methods();

public:
    MyNode();
    ~MyNode();

    void _process(double delta) override;

    void set_speed(double p_speed);
    double get_speed() const;
};

}

#endif

my_node.cpp:

#include "my_node.h"
#include <godot_cpp/core/class_db.hpp>

using namespace godot;

void MyNode::_bind_methods() {
    ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &MyNode::set_speed);
    ClassDB::bind_method(D_METHOD("get_speed"), &MyNode::get_speed);
    ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed"), "set_speed", "get_speed");
}

MyNode::MyNode() {}
MyNode::~MyNode() {}

void MyNode::_process(double delta) {
    rotate_y(speed * delta);
}

void MyNode::set_speed(double p_speed) { speed = p_speed; }
double MyNode::get_speed() const { return speed; }

After compiling (scons → a binary in bin/) → restart Godot → “MyNode” will appear in Add Node with a speed field in the Inspector.

Rust — gdext

The community developed gdext — bindings for Rust. The style is more idiomatic:

use godot::prelude::*;

struct MyExtension;

#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {}

#[derive(GodotClass)]
#[class(base=Node3D)]
struct MyNode {
    #[var]
    speed: f64,
    base: Base<Node3D>,
}

#[godot_api]
impl INode3D for MyNode {
    fn init(base: Base<Node3D>) -> Self {
        Self { speed: 1.0, base }
    }

    fn process(&mut self, delta: f64) {
        let rotation_y = self.base().get_rotation().y + (self.speed * delta) as f32;
        let mut rot = self.base().get_rotation();
        rot.y = rotation_y;
        self.base_mut().set_rotation(rot);
    }
}

Rust advantages:

  • Memory safety without overhead — no malloc/free errors, no UB.
  • Cargo — the package ecosystem is simpler than CMake/SCons for C++.
  • Performance on par with C++.

Downsides:

  • The first compile is a bit slower.
  • Fewer community plugins with examples (than C++).

gdext is actively developing and is considered production-ready as of 2026.

When it’s worth writing a GDExtension

Worth it:

  • Voxel engine, terrain LOD, marching cubes.
  • Custom physics constraints or soft-body simulation.
  • Real-time DSP / audio synthesis.
  • Integration with an existing C++ library (Open3D, OpenCV, ML inference libraries).
  • Workflows where even Burst/SIMD-level GDScript is insufficient.

NOT worth it:

  • Standard gameplay logic (use GDScript).
  • Single-shot operations (generating a map once — write it in WorkerThreadPool from GDScript).
  • When you’re not sure that the bottleneck is in the CPU computations. Profile first.

Performance

Benchmarks for a simple numerical workload (a million iterations):

LanguageTime
GDScript (untyped)850 ms
GDScript (typed)400 ms
C#80 ms
C++ (GDExtension)12 ms
Rust (gdext)12 ms

C++ and Rust are tens of times faster than typed GDScript. But overhead kicks in when crossing the GDExtension boundary (calling a C++ method from GDScript). That’s why you write large chunks in C++, calling them rarely.

Cross-platform — build for each platform separately

GDExtension consists of native binaries. Linux x86_64 doesn’t run on Windows. You need to build for each target platform, which complicates CI/CD. For one platform it’s simple; for shipping to Windows + Linux + macOS + Android + iOS you need 5 different CI jobs.

Extensions commonly written with GDExtension

  • godot-rapier-3d — Rapier physics in Rust (an alternative to the built-in physics).
  • Voxelity — a voxel engine for Godot.
  • godot-luau-script — Luau (Roblox-style) scripting.
  • NAVigator — advanced navigation.

The Asset Library has a “Native plugins” category — with ready-made GDExtension plugins.

An alternative — C# in Godot

If you have a .NET team and don’t want to get into C++, C# via Godot.NET gives a ~10× speedup over GDScript (untyped). See the next chapter.

Comparison with Unity

Unity equivalent:

  • A Native Plugin (.dll / .dylib / .so) loaded via [DllImport].
  • Less integrated — native plugins don’t appear as Node classes in the hierarchy.
  • Unity’s alternative for heavy computation is the Job System + Burst (see the chapter on Jobs+Burst), all inside C# and without leaving for the native world.

GDExtension in Godot is a deeper integration with the engine; Burst in Unity is optimization without leaving C#. Both approaches are valid.