godot-go: writing Godot extensions in Go
Go bindings for Godot 4.7 GDExtension API
At a glance
- What is it?
- godot-go binds the Godot 4.7 GDExtension API to Go through cgo, generating the wrapper code from the engine's own extension_api.json so the bindings stay in step with the binary. It is early and explicitly experimental, the project says the bindings are more than likely leaking memory, and the build needs clang-format, gcc and a matching set of Godot headers.
- Who is it for?
- Use godot-go if you want to write Godot editor tooling or game logic in Go and you are prepared to track an API that the project labels experimental and that is expected to change before 1.0. Do not adopt it for a shipping game, because the README states the bindings are more than likely leaking memory, not all variant types have been tested, and the API is subject to change.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Go inside Godot through GDExtension and cgo
godot-go is a set of Go bindings for the Godot Game Engine, described as bindings for the Godot 4.7 GDExtension API. The project integrates into Godot through the GDExtension API through cgo.
That last mechanism is the whole design in three words. GDExtension is Godot's own mechanism for loading native libraries written in other languages, and cgo is Go's mechanism for calling into C. So a Godot extension written in Go is a shared library built by the Go toolchain with a C shim, which Godot loads the same way it loads a C++ or Rust extension.
The consequences flow from that. A Go extension is not a separate process, it is a library loaded into the engine, and it shares the engine's memory. It also needs a C toolchain, since cgo compiles C as part of the build, and it needs the engine's own headers to know the shape of the API it is calling.
The project is MIT licensed, written in Go, and the module is github.com/godot-go/godot-go. The documentation site is godot-go.github.io, and the README carries CI badges for Linux and Windows. The repository topics list cgo, cgo-bindings, game-development, godot, godot-gdextension and golang, which is an accurate summary of the shape of the project.
The motivation is straightforward. Godot's own scripting languages are GDScript and C#, and a game written in Go was not an option before.
The bindings are generated from the engine's own API description
The mechanism that keeps the bindings honest is the header sync step, and the README treats it as a hard requirement: in order for godot-go to work correctly, you must make sure the godot_headers are in sync between your godot binary and godot-go.
The relevant files are the engine's own. There is gdextension_interface.h, the C header describing the extension interface, and extension_api.json, the machine readable description of the whole Godot API with its classes, methods, properties and constants. The build exports both from the binary you actually intend to run:
GODOT=/some_path/bin/godot make update_godot_headers_from_binaryand then generates the Go wrapper code from them:
make generateDevelopment and testing are done against Godot 4.7, from the godotengine godot-builds releases.
This is a design decision with a clear rationale and a clear cost. Generating from the engine's description means the bindings cannot drift from the API, and it means a new Godot release is a regeneration rather than a hand-written update. The cost is that the headers are tied to a specific binary, which is why the README insists on exporting them rather than copying a checked-in copy, and why the minimum supported Godot version is 4.7.
The repository structure follows the same split. There is a godot_headers directory, a cmd directory, a pkg directory, a runtime directory, a test directory, a docs directory and an openspec directory, plus main.go at the root and a Makefile that carries the whole pipeline.
The build and the four tools it expects
The requirements list is short: clang-format, gcc, go 1.25.x and goimports. Two of those are for the project itself rather than for the bindings.
clang-format is there because the generated C code has to be formatted, and goimports handles import ordering in the Go sources. The Makefile picks up the tools from the path, with fallbacks for older clang-format versions, and it resolves the Godot binary from the GODOT variable, which defaults to a godot found on the path.
The build sequence is:
# Install go dependencies
make installdeps
# generates code for wrapping gdextension_interface.h and extension_api.json
make generate
# build godot-go
make buildThe Makefile turns on cgo explicitly, sets the test entry point at test/main.go, and chooses the output artefact name per platform: a DLL for Windows, a framework for macOS, and a shared object for Linux, with a generic .so fallback.
There is also an escape hatch for extra compiler flags, through EXTRA_CGO_CFLAGS and EXTRA_CGO_LDFLAGS, which matters on musl based systems such as Alpine Linux. There, the C side backtrace printing is compiled out because musl provides no execinfo interface, and the README explains how to supply a glibc compatible execinfo library and point the build at it with flags such as -DGODOT_GO_ENABLE_EXECINFO.
The Go module is declared as go 1.27.0 in go.mod, which is a separate thing from the go 1.25.x toolchain the README asks you to install for development, and the dependency list is small and mostly tooling: cobra for the command line, zap for logging, cgoalloc for the allocation side of the bridge, participle for parsing and strcase for case conversion.
The project tells you what is not finished
The Current State of the Project section in the README is short and unusually candid, and it should be read before anything else.
The project is under heavy development. The API should be considered experimental and is subject to change, and the API is expected to become more stable as the project gets closer to a 1.0 release. You must have a minimum of Godot 4.7. The tests confirm basic functionality. And then the sentence that decides adoption for most people: the bindings are more than likely leaking memory.
That last point is not a detail to be filed away. A Go extension loaded into the Godot editor shares the engine's process, and a leak there is a leak in the editor, which means a long editing session gets slower and the only way out is a restart. The README also notes that not all variant types have been tested, and the variant system in Godot is exactly where a binding layer tends to have gaps, because it is a dynamic value type that the C API exposes as tagged unions.
The project points at godot-go-demo-projects for demo projects running a mix of godot-go and GDScript classes, which is the sensible way to evaluate it: a mixed project tells you what it feels like to interleave Go and GDScript, and what the seams look like.
There are no GitHub releases, so the artefact you get is whatever the branch produces, and the last push was on 2026-10-22.
Testing against the real engine
Because the extension is loaded by Godot, the test loop has to involve a Godot binary rather than a Go test alone. The README describes a two-step flow.
The first step generates the cached files for the test demo project, and the README reassures you not to be concerned if it fails:
GODOT=/some_path/bin/godot make ci_gen_test_project_filesFrom then on, iteration is a single command:
GODOT=/some_path/bin/godot make testThat runs the demo project in the test directory, and the expected output is in the CI logs.
The design is sensible because it mirrors how the extension is actually used. The Go code is compiled into a library, the library is loaded by the engine, and the behaviour is observed from the engine side. A test that only exercised the Go layer would miss the cgo boundary, which is exactly where the interesting failures live: the unsafe pointer conversions between Go memory and the engine's variant types, the allocation and release of objects the engine owns, and the callbacks that cross the boundary in the other direction.
The README's reference list shows where the design drew on prior art. It points at the Go 101 article on type-unsafe pointers, a cheatsheet for cgo data type conversion, the blog post on safe use of unsafe pointer conversion, and the xgo tool for cross compilation with cgo, which is the standard answer to the problem that cgo normally makes cross compilation awkward.
Getting a stack trace out of a native crash
The longest part of the README is about a problem that anyone doing native work in a game engine will recognise: when the extension crashes the editor, you get an address and no source line.
The project has clearly been through this. The captured frames resolve offline to a function plus file and line, and the recommended tool is Go's own resolver rather than GNU addr2line, because GNU addr2line effectively hangs on the Go DWARF in these debug builds:
echo 0x536e61 | go tool addr2line test/demo/lib/libgodotgo-test-linux-amd64.soInside the probe window the Go side also prints its own stack with the go-stack option, which gives live file and line for Go frames on every platform, including the ones where native capture is stubbed out.
The Windows story is the interesting one, because it is a workaround rather than a feature. Capture is built on the native DbgHelp stack-walking API, and it is opt-in for now: a default Windows build compiles it out and does not even import dbghelp.dll, because dbghelp's process-detach path is a suspect in the Windows teardown crash tracked in issue #172. Enabling it takes an extra flag, -DGODOT_GO_ENABLE_DBGHELP, and with it enabled frames print symbol names when a PDB is available.
For frames that come from packages compiled with -gcodeview, the C line information lives in the PDB, and the README recommends llvm-symbolizer with an explicit --pdb, because the path embedded in the DLL points at the build machine. The Makefile explains why only one package opts in: dwarf2codeview in MinGW-Builds GCC 15.2.0 crashes the compiler on the generated pkg/ffi translation units and on the pkg/core cgo preamble, so the CodeView switch is scoped to pkg/log rather than applied globally.
Editorial conclusion
Use godot-go if you want to write Godot editor tooling or game logic in Go and you are prepared to track an API that the project labels experimental and that is expected to change before 1.0. Do not adopt it for a shipping game, because the README states the bindings are more than likely leaking memory, not all variant types have been tested, and the API is subject to change. Before you start, export the headers from the exact Godot binary you will run, keep clang-format, gcc, goimports and Go 1.25.x available, and note that the project publishes no GitHub releases and was last pushed to on 2026-10-22.
Frequently asked questions
What is godot-go and how does it connect to Godot?
godot-go is a set of Go bindings for the Godot Game Engine, covering the Godot 4.7 GDExtension API. It integrates into Godot through the GDExtension API through cgo, so a Go extension is a native library loaded by the engine.
What do I need to build godot-go?
The README lists clang-format, gcc, go 1.25.x and goimports as the requirements, and the go.mod declares go 1.27.0. You also need the Godot headers in sync with your Godot binary, which the build exports with GODOT set and make update_godot_headers_from_binary before make generate and make build.
Is godot-go ready for production use?
Not by the project's own account. The README says the project is under heavy development, the API should be considered experimental and is subject to change, the bindings are more than likely leaking memory, and not all variant types have been tested. The API is expected to become more stable closer to a 1.0 release.
How do I run the godot-go tests?
First generate the cached files for the test demo project with GODOT set and make ci_gen_test_project_files, and the README notes it is not a problem if that step fails. After that, iteration is GODOT set and make test, which runs the demo project in the test directory, with the expected output recorded in the CI logs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/godot-go-godot-go)Was this useful?