Reference
Rust game engines compared: macroquad, Bevy, Fyrox and more
A plain comparison of Rust game engines and frameworks: macroquad, Bevy, Fyrox, ggez, rend3, Comfy, raylib and Godot bindings, and who each one suits best.
Maintained by Kasimir Lund
Pick a Rust game engine and you are really picking a set of opinions: where the origin sits, who owns the main loop, whether your game state lives in an ECS or in plain structs, and how much of the rendering you are allowed to touch. None of these choices is wrong. They just suit different games and different people. This page goes through the engines and frameworks that come up most often when someone asks "what should I use for a small game in Rust", says what each is good at, and ends with a table you can keep open while you decide.
I have tried to keep it version-neutral. Rust game crates move quickly and break their APIs often, so anything here about a specific function name is a description of the idea, not a promise about the crate you will download. Check each project's own documentation before you commit.
How to read the comparison
Five questions separate these engines more than anything else.
- Who owns the loop? Some give you a function you call every frame (you own the loop). Others take a set of systems or callbacks and run them for you.
- Coordinate system. Top-left origin with y pointing down, in pixels, is the classic screen convention. Centre origin with y pointing up, in world units, is the maths convention. Mixing them up is the single most common source of "my sprite is upside down" bugs.
- How is state stored? Plain structs that you pass around, or an entity component system that owns everything.
- Rendering ceiling. Can you get floating point render targets for HDR and bloom? Can you write your own shaders? Is there a render graph?
- Platform reach. Desktop is easy for everyone. Web, Android and iOS are where the differences show.
macroquad
macroquad is a small, immediate-mode framework built on top of miniquad, a minimal cross-platform rendering layer. You write an async main, draw things with plain functions, and await the next frame at the bottom of your loop. That is most of the API surface, and it is a pleasant one.
// Shape of a macroquad program, simplified
loop {
clear_background(DARKGRAY);
draw_circle(screen_width() / 2.0, 120.0, 24.0, YELLOW);
draw_text("score 12", 16.0, 32.0, 24.0, WHITE);
next_frame().await;
}
The default coordinates are screen pixels with the origin at the top left and y growing downwards, which matches what most 2D tutorials expect. Draw order is call order: whatever you draw last ends up on top, so layering is your job.
Its big strength is reach. Because it targets older, widely supported graphics APIs, the same game runs on desktop, in the browser and on mobile with little fuss, and it compiles fast. The cost of that reach is the rendering ceiling: texture formats that more modern APIs take for granted, such as half-float render targets, are not something you can rely on everywhere, which makes proper HDR lighting awkward. For a jam game, a puzzle game or a first Rust project, it is hard to beat.
Bevy
Bevy is the largest community project in this list and the one people mean when they say "the big Rust engine". Everything is built around its ECS: your game is a collection of components stored in a world, and systems are ordinary functions whose parameters tell the scheduler what data they need. Features arrive as plugins, and the official crates are joined by a large ecosystem of community ones.
Its 2D camera uses a centre origin with y pointing up, measured in world units that map to pixels by default. Rendering sits on wgpu, so modern features like HDR and custom shaders are available, through a render graph that is powerful and not small.
The trade-offs are well known. Releases tend to include breaking changes, and community plugins can lag behind, so upgrading a project is a chore you will repeat. Compile times need some care (dynamic linking during development and a fast linker help a lot). And if you dislike thinking in queries and schedules, the ECS will feel like it is between you and your game. If you enjoy that style, or your game has thousands of similar entities, it is a very strong choice.
Fyrox
Fyrox aims at the full-engine category: a scene editor, an asset pipeline, a 3D renderer with lighting and materials, animation, physics, UI and 2D support on top. It is the closest thing in Rust to the Godot or Unity way of working, where you assemble scenes in an editor and attach scripts written in Rust.
That makes it the most obvious Rust-native option for a 3D game, or for anyone who wants a visual editor rather than everything in code. For a tiny 2D game it is a lot of engine to learn. I would look at it if your project actually needs the editor and the 3D toolset.
ggez
ggez is one of the older Rust frameworks, loosely in the spirit of LÖVE: you implement an event handler with update and draw methods and hand it to the framework, which runs the loop. It has gone through periods with little maintenance and then been picked up again, and at some point its graphics moved onto a wgpu backend. Its default 2D coordinates follow the screen convention, top left and y down.
It is a reasonable alternative to macroquad if you prefer the callback style. Before starting anything long-lived, check how active the repository is, because that history of maintainer gaps is the main risk.
rend3
rend3 is not an engine. It is a 3D renderer built on wgpu, with materials, meshes, shadows and a render graph. You bring your own windowing, input, audio and game structure. It is worth knowing about if you are building a 3D game in code and want a ready-made renderer rather than writing one, but it does nothing for 2D projects and you should check its maintenance status before depending on it.
Comfy
Comfy is an open-source 2D engine built on wgpu that took a deliberate middle path: an immediate-mode API close in spirit to macroquad, with more batteries included and a higher rendering ceiling. The feature checklist on this site uses it as one of its reference points.
Its distinguishing choices were:
- Centre origin, y up, world units. The default camera shows roughly thirty world units across, and the intended setup for pixel art is one sprite tile per world unit, then zoom the camera to taste.
- Built-in z-index. Most draw calls take an integer layer, and the engine sorts and batches for you, so draw order stops being a concern in game code.
- HDR by default. Rendering happens into floating point targets and is tonemapped at the end, so lights can be brighter than 1.0 and bloom works from real HDR values.
- egui included, plus audio through kira, particles, trails and a simple 2D camera.
- Narrower reach. It targets desktop and the web through WebGL 2, not the older APIs that let macroquad go everywhere.
Development slowed after 2024. The last tagged release covered on this site is 0.4, from May 2024, described in the notes on version 0.4. Treat it as a well-designed codebase to read and learn from, and as a usable tool for small projects if you are happy to pin a version and fix things yourself.
raylib through Rust bindings
raylib is a C library with a famously simple API: open a window, begin drawing, draw shapes and textures, end drawing. Rust bindings wrap it, so you get that same style with Rust on top. Screen coordinates are top left and y down, with a separate 2D camera type for world space.
The appeal is the enormous amount of raylib learning material and examples, most of which translate directly. The downsides are the usual ones for bindings: a C dependency in your build, an API that feels like C in places, and the bindings may trail the C library. Good for people who already know raylib from another language.
Godot with Rust bindings
If the goal is to ship a game, especially a 3D one, Godot with Rust bindings (through Godot's GDExtension interface) is often the most practical answer. You get Godot's editor, scene system, physics, animation tools and export pipeline, and you write the performance-sensitive or complex parts in Rust.
Many people who try this end up keeping some GDScript for the small glue code, and that is fine: wiring a button to a scene change does not need the borrow checker. The real cost is that you are now working in an editor-first engine with Rust as a guest, which changes how you think about structure. Some developers love that. Others find code-first frameworks more fun and go back to them.
Comparison table
| Engine | Kind | Default 2D coordinates | State model | HDR and custom shaders | Platform reach | Best for |
|---|---|---|---|---|---|---|
| macroquad | Framework | Top left, y down, pixels | Your structs | Limited | Very wide (desktop, web, mobile) | Jams, first Rust games |
| Bevy | Engine | Centre, y up, world units | ECS throughout | Yes, via render graph | Wide | ECS fans, larger projects |
| Fyrox | Engine with editor | Scene based | Scene graph and scripts | Yes | Desktop, web | 3D and editor workflows |
| ggez | Framework | Top left, y down, pixels | Your structs | Some | Desktop | Callback style 2D |
| rend3 | 3D renderer | Not applicable | Bring your own | Yes | Desktop | Custom 3D projects |
| Comfy | 2D engine | Centre, y up, world units | Your structs, optional ECS | Yes, HDR by default | Desktop, web | Small 2D games with lighting |
| raylib bindings | Framework | Top left, y down, pixels | Your structs | Some | Wide | People who know raylib |
| Godot plus Rust | Editor engine | Godot's own | Scene tree and nodes | Yes | Very wide | Shipping, especially 3D |
"Some" and "Limited" are deliberately vague. They mean "possible, but expect to read source code", and the exact answer changes between releases.
A worked example: one sprite, three conventions
Say you place a 16 by 16 pixel sprite "two tiles right and one tile up from the middle of the screen" on a 320 by 180 pixel view. Here is what you would write under each convention.
| Convention | Units | Position you pass |
|---|---|---|
| Top left, y down, pixels | pixels | x = 160 + 32 = 192, y = 90 minus 16 = 74 |
| Centre, y up, pixels | pixels | x = 32, y = 16 |
| Centre, y up, one tile per unit | world units | x = 2.0, y = 1.0 |
The third row is why world units are nice for tile-based games: positions read like grid coordinates, and changing resolution means changing the camera, not every number in your code. The first row is why screen coordinates are nice for UI: a health bar at (8, 8) is always in the corner. Many engines let you have both, with a world camera and a separate screen-space layer.
Choosing, quickly
If you want something running tonight and on a phone next week, use macroquad. If you like ECS or expect the project to grow big, use Bevy. If you need a 3D editor, look at Fyrox or Godot with Rust. If you mainly want to learn how a compact wgpu 2D engine is put together, read Comfy's source alongside the small technique examples here.
Whatever you pick, spend one evening on the same tiny test in two candidates: a moving sprite, a sound on keypress, a text label and a web build. That evening tells you more than any table, including this one.