Notebook icon by Delapouite, available at game-icons.net
Cog icon by Lorc, available at game-icons.net
This survey was open for submissions in Sep 2023. Up until the end of the year. With the sample size being 81 submissions, the survey is not statistically significant. Also, do keep in mind that some tech might be more representative of circles I’m active in, rather than the overall landscape.
I am sorry it has taken me this long to process it, but I had quite an eventful time in the meantime. I also wanted to organise it into pretty graphs, but I think the data has been locked away for too long, so it’s just time to let it be free.
I would like to thank everyone who filled in the form, you’ve certainly helped me and others understand the state handmade engines are in. I have selected a few replies and decided to showcase them, as they might be doing something different or interesting.
So, here are the results:
Engine Language (Multiple Choice)
C++ (36) 44.44%
C (21) 25.92%
Rust (8) 9.87%
Odin (6), C# (6) 7.40%
Zig (5) 6.17%
Jai (2) 2.46%
Racket, Z80 ASM, x86 ASM, Common Lisp, TypeScrypt, Scala, Java, WASM(Not a langauge, iirc) (1) 1.23%
Gamecode Style (Multiple Choice)
Native language, compiled with the game (62) 76.54%
DLL (21) 25.92%
Scripting Langauge (17) 20.98%
Other (7) 8.64%
Subquestion: Scripting langauge
Lua (8) 47.05%
Custom (6) (Mix of visual and typed) 35.29%
Luau (2) 11.76%
Mruby, Wren, Racket, JSON, Scala, Vex Markup, TypeScript, Flecs script, Rhai, GDScript (1) 5.88%
Showcase #1: Kandria
For the first submission showcase, we have Kandria by Shirakumo Games. It’s a movement-strong metroidvania made in the Trial engine, made almost completely in Common Lisp. Practically unheard of in gamedev!
Here’s what it’s creator Yukari, has to say about it:
I started working on the game engine, Trial, out of fun. At some point I decided I wanted to make something more serious with it in an attempt to dogfood it. That thing turned into Kandria.
I’ve outlined the pros and cons [of using Common Lisp] in two talks/papers: GIC21 and ELS23. The short is: interactive development owns. Being able to change any part of the game and engine at runtime makes iteration fast. Being a small community means reinventing a lot of stuff yourself, though.
Trial runs on Nintendo Switch, however it’s not feature complete yet. Getting SBCL to play nicely with Nintendo’s toolchain was a struggle, but you can read more about it on Yukari’s blog!
Another un-commonality is that Trial uses a bespoke sound system, Harmony, which is powered by Yukari’s own libmixed audio library.
Last thing of note to mention would be PromptFont: a font that includes various button prompts for modifier and control keys and gamepads. Yukari has kindly released it under a permissive license that only asks for attribution.
Yukari is currently working on a 3D game, and I’m looking forward to seeing the results!
If you wish to support a self-funded indie dev, you can buy Kandria on Steam and follow Yukari on Mastodon!
Supported Platforms (Multiple Choice)
Windows (76) 93.82%
Linux (52) 64.19%
MacOS Intel (29) 35.80%
MacOS ARM (25) 30.86%
Android (7) 8.64%
Nintendo Switch (6) 7.40%
Microsoft Xbox One, Sony Playstation 4, Sony Playstation 5 (4) 7.93%
Microsoft Xbox Series X | S, iOS, Web (3) 3.70%
Raspberry Pi (2) 2.46%
MS-DOS, FreeBSD, Haiku, Windows 3.51 (1) 1.23%
Showcase #2: Kitsune Tails
Next up for the showcase is Kitsune Tails by Kitsune Games! A SMB3-reminiscent platformer inspired by Japanese mythology. The game is out on PC, with console ports in development!
I spoke to Eniko who was kind to shed some light on Kitsune’s engine, which uses both Lua for scripting and Kitsune’s own level-scripting language called LayerScript.
I came up with the framework many years ago when I wanted to program in C# but also wanted to deploy on web. The idea was that I could use OpenGL in .NET and WebGL on the web, and use a C# to Javascript transpiler (Saltarelle Compiler, now defunct) to convert my C# code to work on the web. I created an abstraction layer for everything a game needs (this includes stuff like randomization, regular expressions, date time, etc) and harmonized it so that the outputs between .NET and web were consistent.
Initially I used OpenTK on the .NET side. Eventually I switched to SDL2, using SDL_GL_GetProcAddress to interface with OpenGL and utilized parts of FNA for sound, input, and video. There's also a lot of bespoke code like (animated) GIF, PNG, and TGA file format support, custom JSON parser, a deflate implementation, coroutines, easing/tweening, localization, and more.
Our priorities are changing with the deprecation of OpenGL so I've recently created a new version of the framework that sits on top of stock FNA in order to ensure the longevity of our games.
SDL2 and FNA are made to work easily on Nintendo Switch and the Xbox. PlayStation is a lot trickier (…) but essentially being built on top of SDL2 and FNA makes console porting significantly easier.
I was rather curious about the mix of scripting languages, to which Eniko had this to say:
The Lua scripting parts were made for me, Kitsune Tails' coder, and for modders who also know how to code. LayerScript is intended for level designers, who may not know how to code. Our lead level designer for example, Elizabeth Dumler, isn't a coder. So I made LayerScript with a very simple syntax so that basic operations are very easy even for non-coders to understand. For example this is a script for repeatedly moving a layer up and down by 1 tile over 0.5 seconds with a 1 second pause in between:
up 1 0.5
pause 1
down 1 0.5
pause 1While I could've achieved something similar with Lua, probably using coroutines of some kind, this syntax is way simpler because it's bespoke.
If this looks like your jam, check out Kitsune Tails website, and follow Kitsune Games on Mastodon and Bluesky and you can follow Eniko on Mastodon and Bluesky as well!
Platform APIs Used (Multiple Choice)
libSDL (36) 44.44%
Win32 (35) 43.20%
X11 (10) 12.34%
GLFW (6) 7.40%
Wayland (3) 3.70%
raylib, winit, Monogame (2) 2.46%
sokol_app, Android NDK, Java Cross Platform API, Allegro, ncruses (1) 1.23%
Rendering APIs & Middleware Used (Multiple Choice)
Vulkan (20) 24.69%
OpenGL3+ (GLAD, GLUT, GLEW) (19) 23.45%
OpenGL3+ (wgl, egl, cgl, glx) (17) 20.98%
libSDL (15) 18.51%
Software (custom) (13) 16.04%
DirectX 12 (12) 14.81%
DirectX 9 - 11 (10) 12.34%
OpenGL pre-3 (GLAD, GLUT, GLEW) (4) 4.93%
OpenGL pre-3 (wgl, egl, cgl, glx), WebGPU (3) 3.70%
raylib, Metal (2) wgpu (2) 2.46%
bgfx, Software (thirdparty), Sokol_gfx, Dawn, Dusk, Monogame, WebGL2 (1) 1.23%
Personal Comments
There were a few submissions that replied “None”, but they were targeting specific retro platforms, like SEGA Master System or MS-DOS. They have been bundled into Software (custom), as it is the CPU’s responsibility to manage how sprites are loaded into VRAM on these platforms.
Showcase #3: Solar Storm
Solar Storm is a turn-based artillery-style battler. If you liked Scorched Earth or Pocket Tanks, you’ll enjoy Solar Storm as well!
The engine is written in Odin, a langauge that I only marginally knew about, so I spoke to Jakub about it.
When I started working on Solar Storm, I had been already using Odin for nearly 3 years. At that point it was my language of choice for all gamedev or graphics related projects. (…) There are a lot of small quality-of-life improvements which add up, and it's much less friction in total.
A big drawback [of using Odin] is using external libraries, usually the API is in C so you're pretty much forced to write bindings. Turns out this wasn't such a huge problem for me, I had already written all the bindings for libs I needed before starting the project. And even then it's surprisingly little work to write bindings even for big libraries. However it's a big disadvantage.
A very unexpected advantage is the Odin community. Because it's such a small language everyone knows each other, it's very easy to talk to compiler devs or core lib contributors.
Another pretty big advantage is Odin's core lib. There are many things to get a project started quickly, but more importantly the source code is fairly simple and readable, so it's trivial to fork a particular package and modify it to your needs.
Solar Storm’s engine uses Sokol_app as its platform middleware, and with it being the only submission that uses it, so I just had to ask about it.
The reason I picked [sokol_app and sokol_gfx] is because each of those libs is just a single, big C header file. This makes it very nice to work with - it's trivial to find the implementation of whatever you need, and it makes it very easy to integrate into your build system/script. (…)
Another advantage is sokol-shdc, which is a cross-platform shader compiler for a flavor of GLSL.
In my current project I use sokol_gfx with SDL just because it's a bit more feature complete, stable and has a better platform support. However sokol_app is definitely good enough for most small games or lightweight apps.
A fun standout feature of the engine is that the engine is written so its components are ‘immedate-mode’. If you’re wondering how this looks, think Dear ImGui, but for all parts of the engine.
The main reason why I like immediate-mode APIs is because they don't force you into a particular control flow structure at all. If I want to render a text, I can just call something like
draw_text(fmt, coord, color, alignment)
every frame, instead of creating a stateful "TextRenderer" component or something. This makes dealing with dynamic state way easier.
Turns out if you make some tradeoffs it's possible to use immediate-mode APIs for pretty much everything. Everyone knows ImGUIs, but using this paradigm for things like rendering, collision, physics and all a whole bunch of gameplay things is very nice.
Some systems seem like a very retained-mode problem at first, for example physics engines. If possible it's nice to design your system in a way that doesn't need to track any data whatsoever between frames. My current rendering engine has a bunch ofrenderer_draw_*
procedures for different things, but they just append a GPU-ready instance data to a buffer which gets uploaded to VRAM later, before the actual rendering.
But if you do need to track data between frames, you need to make sure to have some kind of ID system in the API, it's a pain to change later.
You can check out Solar Storm’s website, or give Jakub a follow on Twitter, Bluesky or YouTube.
He’s currently working on a Quake-inspired retro FPS called BEHEADER, which is also created in Odin.
Audio APIs & Middleware Used (Multiple Choice)
SDL Audio (14) 17.28%
FMOD (11) 13.58%
SoLoud (9) 11.11%
OpenAL (8) 9.87%
MiniAudio (7) 8.64%
DirectSound (6) 7.40%
rodio (4) 4.93%
SDL_Mixer (3) 3.70%
WASAPI (2) 2.46%
Wwise, Sokol, AAudio, libmixed, harmony, portaudio, mach-sysaudio, XAudio (1) 1.23%
Other Libraries used (Text Box)
stb (17)
Dear Imgui (9)
FreeType (6)
libVorbis, libogg (5)
miniz, lz4 (4)
glm, rapidjson (3)
Jolt, zlib, Steamworks, Quite Ok Image (2)
Personal Comments
Since the users were free to enter whatever, I’ve opted for just listing libraries that were mentioned more than once.
There is an interesting divide: most submissions contained either a large list of libraries, or something low like 1 or 2.
Most people that said “stb” did not specify *which* stb header they were using, but those that did specify, always listed multiple, so I’m bundling all of them.
On the other hand, some people said just “imgui” (12), but quite a bit of people specified “Dear Imgui” (9). So, erring on the side of caution, I’m excluding the unspecified ones.
Editor Style (Single Choice)
Separate Tools (42) 51.85%
Unified Editor (31) 38.27%
“Just Text Editor”, “Engine is a library”, and other similar (6) 7.40%
“In-game map editor, separate for everything else” (2) 2.46%
Showcase #4: Costa Verde Transport Department
And for the final showcase, it’s Costa Verde Transport Department by Nikita Lisitsa! If you enjoy vibrant strategy builder/sim games, you will surely enjoy this hexagonal sprawling-with-life road-builder!
It’s engine, Psemek, is on the showcase here as an example of “engine as a collection of libraries”. The games made with it don’t utilise editors, but rather generated or written files.
I generally prefer to have as much decoupling as possible, it makes the code much cleaner and reusable, and speeds up compilation times. For example, I have a cpu-side vector graphics drawing library which I often use to generate icons, UI sprites, and stuff like that. This library doesn't use any OpenGL stuff, doesn't depend on SDL2, audio, or a ton of other things, so it's incredibly simple to use it in a simple self-contained executable.
[An editor] doesn't really make sense for my engine anyway! I don't have a pre-built "graphics engine", I just have utility wrappers around graphics APIs (OpenGL and WebGPU). I don't have any specific scene hierarchy format, physics, or anything like that. (…) This allows me to experiment freely with how to structure the code for each project independently.
For example, most of my jam games are just a "world" struct with an std::vector per each object type, and the graphics is just a few basic shaders that draw these objects.
For another example, my current "main" project, a village building sim, uses the ECS and uses fast iteration on objects to stream object instance data to the GPU and renders all the objects in a single MultiDrawIndirect call, and has some simple ad-hoc physics implementation suited for the needs of this particular game.The drawbacks boil down to not having a lot of stuff out-of-the-box, the benefits boil down to much higher flexibility and freedom of experimentation, which is perfect for an indie dev like me.
Another unusuality about Nikita’s workflow is that they use Modern C++, so I had to ask about that.
I've been using C++ for about 15 years, and I genuinely love this language, and I think that most "modern C++" features enable you to write better, safer, faster code, simple as that. I know that opinions on C++ are somewhat controversial (to put it mildly) in the gamedev community, but I love it and it works great for me. It does trade high mental load for the ton of extra features, and I do understand that it can't suit everybody.
Most of my projects have std::vector's everywhere, simply because it's extremely convenient and hard to beat performance-wise. Exceptions are great for serious, unrecoverable errors, since they automatically destroy all the objects on the way, thus cleaning up memory, file handles, GPU resources, stuff like that.
Smart pointers are great for preventing memory leaks. Especially std::unique_ptr is great because it clearly indicates that this is the place that the object is exclusively owned by.
std::filesystem and std::chrono are great (though the API might be a bit cumbersome), I've always used them instead of any other platform-specific solutions.
Standard hash maps (std::unordered_map) are not the best possible solution for some tasks, but given that they're readily available, they are always a good first drop-in solution. In fact, I've made a ton of projects where std::unordered_map was my only spatial optimization data structure, and the performance was always fine.
Lambdas are great for cases when you need to customize some particularly complex piece of code.
Templates (including variadic templates) are very useful for random reusable stuff. In fact, my ECS implementation is essentially a huge variadic-template monstrosity (though it does use custom hash tables with open addressing and quadratic probing, for extra efficiency).
I could recommend this resource as a starting point into modern C++.
And for what’s in the future for Nikita and psemek:
Honestly, the last few days I've been working on a tiny CPU rasterization project, which I'm hoping to turn into a tutorial series.
Editor’s Note: In between me asking and publishing of this, Nikita finished that. You can find it here.
On a larger scale, I'm working on the aforementioned medieval village building game. As I've said, it uses ECS for storing the world data, WebGPU for all rendering, vector graphics lib for pre-rendering UI sprites, my audio library for audio needs, and it uses many of the lower-level libraries as well (general programming utilities, vector & matrix maths, logging, random generation, etc).
Big thanks to Nikita for answering my questions! You can find him on Twitter, Mastodon, BlueSky, YouTube, or read his blog! Psemek, the engine is available on BitBucket.
And don’t forget to check out Costa Verde Transport Department on Steam!
Closing Words
I genuinely did not expect so many people to fill in the survey! I expected about 10 people to fill it in, but when 81 replies came in, I was shocked and pleased. I am surprised about some answers, and very much unsurprised at others (yeah, of course C++ is the most common language). I’d very much like to hear what you think about the results!
I hope the showcases were insteresting as well. I tried to get engines with something unusual about them, because I like to read about and try using uncommon tech. Even if I don’t end up adopting it for my needs they expand my horizons and shift my way of thinking, which helps a lot with future projects. It is definitely something I can recommend to everyone.
It may feel like handmade engines are a dying species, but that’s not true. Game engines just don’t have as much marketing money dumped into them as games, and there’s just 2 engines that do that: Unity and Unreal. The rest of us just happily code away at our desks, creating games the way we like to make them, untethered, free, learning more each day.
And a survey like this is something I wish I had when I started gamedeving, to see what people enjoy using, and play around with it.
I don’t think I’ll be doing another engine survey soon, but I would like to do it again in a few years, see how the landscape changed.
And since I’m posting this on the 31st: Happy New Year everyone! May 2025 bring you much joy as you make games! :)