GATE
GATE is a GDScript superset for the Godot Engine that adds extra features while compiling down to plain GDScript.
Requires Godot 4.7+. MIT licensed.Valid GDScript is valid GATE. Rename a .gd to .gate and it compiles with no errors,
loads, and behaves identically. Recompiling GATE's own output gives back the same bytes.
Questions, or want to show what you built? https://discord.gg/mzW5kY8Zj7 Bugs and feature requests go in issues: https://github.com/Blade67/GATE/issues
Install
Copy addons/gate/ into your project, then enable GATE under
Project Settings -> Plugins. The plugin watches the filesystem and compiles every
.gate file to a .gd beside it whenever anything changes.
To convert an existing script, rename foo.gd to foo.gate and leave foo.gd.uid
where it is. GATE regenerates foo.gd and never touches the uid.
Usage
Write .gate, get .gd. The output is ordinary GDScript with a header naming its
source, plus a .gd.map recording which source line each output line came from.
# player.gate
extends CharacterBody2D
pub int health = 100
Node2D? target
func hurt(amount: int) -> void:
health -= amount
target?.notify_damage(amount)
# player.gd, generated
extends CharacterBody2D
var health: int = 100
var target: Node2D = null
func hurt(amount: int) -> void:
health -= amount
if target != null:
target.notify_damage(amount)
The output is indented the way the rest of your project is. Delete the header, then the
.gate file, and what remains is a normal script you can maintain by hand. There is no
runtime, no dependency, and nothing to ship.
Features
Type-first declarations. The type goes before the name, so the typed form is shorter
than the untyped one. var still works exactly as it does in GDScript, and the short
names are spellings rather than new types: str is String, same value, same
methods, no wrapper.
int hp = 100 # var hp: int = 100
str label = "player" # var label: String = "player"
int[] scores # Array[int]
{str, int} counts # Dictionary[String, int]
Null safety. T? is a tracked nullable type. A flow-sensitive pass follows guards,
early exits and assignments, and rejects a dereference it cannot prove is safe. It only
ever complains about types you declared nullable, so plain GDScript cannot trip it.
Structs. struct has value semantics, class has reference semantics. A struct of
two to four same-typed int or float fields becomes a Vector2/3/4 and costs nothing;
anything else becomes a class and GATE inserts the copies that keep it a value.
Interfaces, traits and namespaces. Interfaces are checked at compile time and testable at runtime. Traits inline their members into the implementor. Namespaces lower to inner classes.
Global classes. @global on a top-level class gives it its own Godot script carrying
class_name - a real, attachable script that shows up in the Create Node dialog, rather
than an inner class of the .gate's own output.
Generics. Monomorphised, so Pool<Bullet> holds a real Array[Bullet] rather than
an untyped one, and is Pool<Bullet> holds across files. Functions take type parameters
the same way.
More types. Unions, tuples and typed callables. type Hp = int names a type once.
PackedScene<Enemy> makes instantiate() return an Enemy. is narrows, as is
checked, and match takes type patterns.
Expressions.
var name = player?.profile?.name # safe navigation
var hp = saved ?? 100 # null coalescing
var [x, y] = get_pos() # destructuring, with types if you want them
var msg = f"{player} scored {pts}" # f-string
for i, item in enumerate(items): # index and value
nums.map((x: int) => x + 1) # arrow lambda
p.xy = Vector2(5, 6) # swizzling
Conditional compilation. @if, @elif, @else and @endif, with the symbols read
from Godot and from your export preset.
Settings. Five spellings are switches under Project Settings > Gate. Type First and Shorthand Types are on by default; Type Names As Values, Multidim Arrays and the experimental Pipe Operator are off. Writing something a switch is off for is an error naming the setting and the spelling to write instead.
In the editor
With the plugin enabled, Godot treats .gate as a language of its own: its own icon,
syntax highlighting, completion, Ctrl-click and F1, working breakpoints, and Godot's own
unused and shadowed-variable warnings reported against the .gate rather than against
the file it generated. Attach Script writes the .gate for you. Comments, ## docs
included, are carried into the output, so your class reference survives.
Two things deliberately do not work. Autocompletion offers nothing rather than guess where the type left of the dot cannot be worked out, and Ctrl-click only follows what GATE can type.
Editing a .gate in another editor
GATE speaks LSP, so a .gate can be edited in Neovim, Helix, Zed or VS Code with
completion, diagnostics, go to definition, hover and an outline. The server runs inside
the Godot editor, the way Godot's own GDScript language server does. It is off by
default: turn on gate/lsp/enabled.
Performance
The generated code is not slower than the GDScript you would have written: across 110 benchmarks the median ratio is 0.98, inside a 1.6% noise floor.
Where GATE is faster, it is because of a lowering you would not write by hand. A struct
local that provably never escapes its function becomes one plain local per field, with no
allocation and no copy - 13.5x on a 300k-iteration loop. An array of structs that
packs into a Vector is 9.9x faster than the idiomatic Array[SomeClass].
Builds are incremental and cached in .godot/gate/, so opening a project nothing has
changed in compiles nothing.
Known limitations
- Godot's debugger panels name the generated
.gd. Breakpoints and stepping are shown in the.gate. - A comment inside a lambda body or grouping parentheses is dropped.
asis only checked in GATE's own declarations. A known unsound spot in the null analysis.- Aliasing is judged by type, and static fields are tracked only where GATE indexed them.
- Interfaces do not fall back to structural checks.
x is SomeInterfaceis false for a class GATE did not compile. - Namespaced classes cannot be attached to nodes as their script.
- Changing a declaration in a file most of the project reaches rebuilds all of it.
- No game has shipped with it.
The full list is in the README, and it is longer than this one.
Verification
GATE is developed against a 26-stage test suite: differential execution against Godot itself, a real-world superset corpus, build-pipeline scenarios across editor restarts, live editor sessions, sourcemap fidelity, byte-exact output comparison and randomised input. The differential harness is the one that matters. It does not ask whether GATE accepted the file, it asks whether Godot's answer equals GATE's answer. Every silent-wrong-value defect in this project was found that way and none were found any other way.
Documentation
The documentation in this repository was written by AI and then checked by me before it shipped. Every claim about behaviour is one I verified against the test suite or the compiler itself, and every number quoted is one that came out of a run rather than an estimate. Where it is still wrong, that is a mistake I missed, so please open an issue and I will fix it.
Changelog for version 1.2.0
2026-09-29 19:29:37
No changelog provided for this version.