Important
Since the 09.11.2026 video by Gamefromscratch was released, this project has received a bit more attention again. A small piece of information regarding the apparent standstill from the outside: the project is not dead. There’s simply not much more to do. Currently, it mainly needs a lot of testing. And testing takes a lot of time, which I don’t have due to my private life. If Godot 4.8 is released, I will of course make sure to make Grit compatible. If you have any questions or find any bugs, or if you can help with testing or find bugs or problems there, please open an issue.
Ahead-of-time compilation of GDScript to native code for Godot 4.7
Warning
Grit is highly experimental. It needs a patched engine, custom export templates and has so far only been tested on Linux x86_64 and Windows x86_64. Expect rough edges, do not ship games with it yet and always keep a way to fall back to the regular GDScript VM.
Grit is a Godot engine module that turns the GDScript of your project into C++ when you export it. That C++ is compiled into your export template, so the game runs the same scripts as native machine code instead of bytecode.
Nothing changes in the editor: you write, run and debug GDScript exactly as before. Native code only exists in the exported game, and every function Grit cannot handle keeps running in the GDScript VM.
- Broad GDScript coverage: all 1540 functions in Godot's own GDScript test suite compile to native code, and all 499 test assertions pass when running natively.
- Unboxed values: typed
int,floatandboolas well as value types such asVector2,Color,StringorTransform3Dlive in plain C++ variables instead ofVariant. - Direct engine calls: operations the VM validates at compile time call Godot's pointer APIs directly. Engine operators, member accessors and constructors are inlined when Grit can prove they are exactly what the engine uses.
- Direct script calls: calls between functions of the same script file skip the engine's dispatch and jump straight into the native function, with the same override, object lock and recursion checks as the VM.
- Engine properties: reading and writing properties such as
positionornode.rotationcalls the engine's getter or setter directly with unboxed values. Object properties such assprite.textureskip the generic lookup too, on reads and on writes where the assigned value's type is known to fit. Each access site caches the resolved accessor per class, and objects whose scripts intercept a property with_get,_setor a member of the same name keep the VM's behavior. - Script members: reading and writing member variables of other script instances, such as
enemy.health, goes straight to the instance's member storage whenever no getter, setter or type conversion is involved. - Script stripping: optionally removes the code of natively compiled functions from the exported scripts.
- Built-in scripts: scripts embedded in scenes and resources are compiled as well, not only
.gdfiles. - Containers: fast element access for
Array, packed arrays andDictionary, with the same bounds and type checks as the VM. - Full language support: lambdas (with captures and
self),awaitand coroutines, signals, inner classes, static functions and variables, default and rest parameters. - Same behavior: runtime errors, their messages and edge cases such as integer overflow or division by zero behave like in the VM.
- Debugging: native functions appear in script backtraces,
print_stack()andget_stack()with the correct lines. - Safe fallback: unsupported functions, edited scripts or mismatching signatures silently use the VM, per function.
- Engine patch. A small patch to the GDScript module (see
patches/) adds a hook toGDScriptFunction::callthat can jump into native code and lets Grit plug in its own code generator. - Recording at export. When you export, Grit recompiles every
.gdfile of the project and every script embedded in a scene or resource. Its code generator forwards everything to the normal bytecode generator and records the same instructions into an intermediate representation. The recording mirrors the VM decisions exactly, which is how the semantics carry over. - C++ generation. The emitter turns that representation into C++ and writes it to
.godot/grit/generated. - Template build. The generated C++ is compiled into a custom export template through the regular Godot build.
- Attaching at runtime. The exported game still loads and compiles its scripts as usual. Whenever a function is compiled, Grit looks up native code for it by script, class, function name and line, checks its signature and attaches it. A fingerprint of all script sources makes sure the template and the exported project belong together.
Coroutines are compiled as native coroutines: at an await, the native code stores its variables in the engine's
function state and continues natively when the signal fires.
Measured with the benchmark project in tests/projects/benchmark on an AMD Ryzen 9 7950X3D, Linux, GCC 16 and a
release export template. Both columns come from the same binary; the VM run uses GRIT_DISABLE=1.
| Benchmark | What it does | VM | Grit | Speedup |
|---|---|---|---|---|
sum_to |
typed integer loop | 196.3 ms | 4.0 ms | 49.1x |
collatz_steps |
nested loops with integer math | 1155.8 ms | 27.8 ms | 41.6x |
count_primes |
trial division | 202.0 ms | 10.3 ms | 19.6x |
mandelbrot |
floating point math | 346.4 ms | 9.8 ms | 35.3x |
particles |
Vector2 math with arrays |
31.7 ms | 7.5 ms | 4.2x |
count_keys |
untyped dictionary updates | 66.6 ms | 42.8 ms | 1.6x |
build_text |
string building | 20.5 ms | 20.2 ms | 1.0x |
spawn_nodes |
creating and freeing nodes | 23.4 ms | 21.6 ms | 1.1x |
node_properties |
moving and rotating 1000 nodes | 65.2 ms | 26.5 ms | 2.5x |
self_properties |
a node moving itself | 50.6 ms | 21.8 ms | 2.3x |
Typed, computation heavy code gains the most. Code that mostly calls into the engine, such as string building or node creation, spends its time inside Godot itself, so compiling the script changes little there.
Clone this repository first:
git clone https://github.com/DasDarki/grit.git
cd gritGrit builds from source. You need everything required to build Godot from source (Python 3.9+, SCons 4.0+ and a C++ compiler) plus git. A full build takes a few minutes on a modern machine. Check your system first:
python3 tools/grit.py doctorIt reports whether Python, SCons, a C++ compiler and git are present, and where the Godot source and editor are.
To type grit instead of python3 tools/grit.py, install a launcher on your PATH:
python3 tools/grit.py installOn Unix this drops a small grit script into ~/.local/bin; on Windows it writes a grit.cmd. The launcher finds a
working Python itself, and the build runs SCons under that same Python, so you do not have to pick an interpreter.
install --uninstall removes it again. The rest of this README uses grit; without the launcher, prefix the commands
with python3 tools/grit.py.
# Clone Godot 4.7.2-stable next to this repository and apply the Grit patch.
grit setup
# Build the Godot editor with the Grit module.
grit build editorBy default the engine lives in ../godot-4.7; use --engine to pick another location, or
setup --godot-repository <path> to create a worktree from an existing Godot clone. Additional SCons options can be
appended to any command, for example linker=lld.
grit game path/to/project "Linux"This is the whole flow in one step. It checks out and patches Godot and builds the editor if that has not happened
yet, exports the project (which generates the native C++), builds the export template with that code, and writes a
ready-to-run game plus a .tar.gz archive into build/games/<name>/. "Linux" is the name of an export preset of
the project. Add --target template_debug for a debug build, --output <dir> to choose the location or --no-archive
to skip the archive.
If you want the pack and template separately, use export instead:
python3 tools/grit.py export path/to/project "Linux" build/game.pck
./build/game.x86_64This exports the project with the Grit editor, which generates the C++, builds the export template with that code and
places it next to the pack. Add --target template_debug for a debug build.
The export log lists every function that stays in the VM together with the reason. To compare against the VM, run
the game with GRIT_DISABLE=1.
You can also export from the editor UI. The C++ is then written to res://.godot/grit/generated; build a template from
it and select that binary as the custom template in your export preset:
python3 tools/grit.py build template_release --generated path/to/project/.godot/grit/generated| Setting | Default | Description |
|---|---|---|
grit/export/enabled |
true |
Generate native code when exporting. |
grit/export/generated_directory |
res://.godot/grit/generated |
Where the generated C++ is written. |
grit/strip_scripts (export preset option) |
false |
Remove the bodies of natively compiled functions from the exported scripts. |
GRIT_DISABLE=1 (environment) |
unset | Run the exported game entirely in the VM. |
With the export preset option grit/strip_scripts, release exports no longer ship the code of functions that run
natively. Grit replaces their bodies, including inline property setters and getters, with a stub that only keeps the
declaration valid, both in .gd files and in scripts embedded in scenes and resources. Class structure, members,
signals, constants and function signatures stay, because the engine and other scripts still need them. Functions that
stay in the VM or contain lambdas keep their bodies.
Stripping also replaces the readable res://<file>::<function>:<line> keys that map native code to scripts with short
hashes, so the export template no longer reveals that map. Script paths and inner-class names can still appear where the
engine needs them to load a class.
A stripped game cannot fall back to the VM. It only runs with the export template that was built together with the
export: with any other template it stops at startup with an error, and GRIT_DISABLE is ignored. Debug exports are
never stripped.
# Run Godot's GDScript test suite with native code.
python3 tools/grit.py conformance
# Export the benchmark project, then run it in the VM and natively and compare results and timings.
python3 tools/grit.py export tests/projects/benchmark Linux build/benchmark.pck
python3 tools/grit.py compare build/benchmark.pck
# Export with stripped scripts and compare it against the VM run of the normal export.
python3 tools/grit.py export tests/projects/benchmark "Linux Stripped" build/stripped/benchmark.pck
python3 tools/grit.py compare build/stripped/benchmark.pck --reference build/benchmark.pckTo profile a single benchmark, run the exported game with -- --only=<name> and repeat it with --repeat=<count>,
for example ./build/benchmark.x86_64 --headless -- --only=node_properties --repeat=20. Exporting with
extra_suffix=profile debug_symbols=yes builds a separate template with symbols for tools such as perf.
grit package <dir> copies the built editor and templates into a directory, for example to share a build with
someone else.
tests/projects/flappy is a small Flappy Bird clone that uses Grit the way a real game would: an inner class for the
pipes, typed arrays, vector math, signals with lambda handlers and an await on a timer. It needs no assets, everything
is drawn in _draw(). It ships Linux, Linux Stripped and Windows Desktop export presets, so on a Windows machine
you can build it the same way with grit game tests/projects/flappy "Windows Desktop".
python3 tools/grit.py export tests/projects/flappy Linux build/flappy.pck
# Play it with space, a mouse click or touch.
./build/flappy.x86_64
# Watch the autopilot play.
./build/flappy.x86_64 -- --autoplay
# Compare the VM and native code.
python3 tools/grit.py compare build/flappy.pckWhen started headless, the game lets the autopilot play 120000 physics steps with a fixed seed and prints the result
and the time, which is what compare uses. The simulation currently runs about 2.5x faster natively.
- Developed and tested on Linux x86_64 with GCC and Clang, and verified on Windows x86_64 with MSVC: the whole GDScript test suite compiles and passes natively there (1540/1540 functions, 499/499 assertions) and the benchmark project produces identical results to the VM. macOS, mobile and web are untested.
- Requires Godot 4.7.2 with the Grit patch and custom export templates.
- Native code only runs in exported games; the editor always uses the VM.
- Breakpoints and stepping in native code are implemented for debug templates but have not been tested with the remote debugger yet. The debugger shows no local variables for native functions, and native functions do not appear in the profiler.
- A few rare constructs stay in the VM, for example lambdas or
awaitinsideassert.