Description
Changelog
Reviews (2)

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.
  • as is 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 SomeInterface is 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.

Reviews

Recommended by Lucas Carlos - 11 September 2026
Recommended by Moshi Berk - 10 September 2026

Login to write a review.

Consider supporting the creators!

If you enjoyed this asset consider supporting its creator. Follow the link below.