I’m building an engine-neutral rendering boundary in Go
I’m developing Cloudstrike, a retro-style shooter written in Go. It currently runs on Windows, Linux and Android with Ebitengine.
Ebitengine has worked very well for the project. I’m not moving away from it because it is bad. I’m separating the game from the engine so I can test additional backends and future platforms without rewriting gameplay code.
The new design uses a small rendering command layer. Gameplay code emits engine-neutral commands:
- sprites with opaque texture handles
- triangles
- rectangles
- explicit ordering and fixed-capacity command buffers
The Ebitengine-specific code now lives in an adapter that resolves texture handles and executes the commands. The intended next experiment is a Raylib backend using the same interface.
The important constraint is avoiding allocations in the game loop. The command buffers are preallocated and reused. Current benchmarks show:
Gameplay update: 0 B/op, 0 allocs/op
Render queue construction: 0 B/op, 0 allocs/op
The backend is benchmarked separately because engine drawing may allocate internally. For example, a large Ebitengine sprite batch still shows backend-side allocations even though the gameplay and queue-building paths remain allocation-free.
This separation has been useful because it makes the costs visible:
gameplay logic
|
engine-neutral render commands
|
Ebitengine backend / future Raylib backend
I’m migrating the code incrementally. Terrain, particles, player visuals, enemies, shields, explosions and major boss elements already use the boundary. UI and some finale presentation paths remain.
As a solo developer, I’m trying to keep the abstraction intentionally small. It only models capabilities the game actually uses instead of attempting to create a general-purpose graphics API. That keeps the interface understandable and avoids paying for flexibility I do not need.
The goal is not to replace Ebitengine immediately. It is to make the engine a replaceable implementation detail, so I can evaluate Raylib, new compile targets and different platform constraints while keeping the Go gameplay code stable.
Posted Aug 28, 2026, 7:46 AM UTC