10 releases (breaking)
Uses new Rust 2024
| 0.10.0 | Feb 27, 2026 |
|---|---|
| 0.9.0 | Feb 16, 2026 |
| 0.8.0 | Jan 25, 2026 |
| 0.7.0 | Jan 11, 2026 |
| 0.3.1 | Jun 25, 2025 |
#553 in GUI
Used in 2 crates
25KB
511 lines
Why Egor?
Egor is dead simple, lightweight and cross-platform. The same code runs on desktop, web (WASM) and mobile with minimal boilerplate. It's built from small, composable crates on top of modern graphics and windowing abstractions
Egor gives you the essentials for 2D apps and games:
- Efficient 2D rendering (shapes, textures, text)
- Keyboard & mouse input
- Camera & world-space transforms
- Interactive UIs with optional egui integration
- Optional hot-reload during development
Platform Support
| Target | Backend(s) | Status |
|---|---|---|
| Windows | DX12, Vulkan, OpenGL | ✅ Stable |
| MacOS | Metal, Vulkan (MoltenVK) | ✅ Stable |
| Linux | Vulkan, OpenGL | ✅ Stable |
| Web (WASM) | WebGPU, WebGL2 | ✅ Working |
| Android | Vulkan, OpenGL | ✅ Working |
Getting Started
Add egor to your project:
cargo add egor
Example:
let mut position = Vec2::ZERO;
App::new()
.title("Egor Stateful Rectangle")
.run(move |FrameContext { gfx, input, timer, .. } | {
let dx = input.key_held(KeyCode::ArrowRight) as i8
- input.key_held(KeyCode::ArrowLeft) as i8;
let dy =
input.key_held(KeyCode::ArrowDown) as i8 - input.key_held(KeyCode::ArrowUp) as i8;
position += vec2(dx as f32, dy as f32) * 100.0 * timer.delta;
gfx.rect().at(position).color(Color::RED);
})
For full documentation see the official docs
Running a Native Build
Simply run cargo:
cargo run
Running a WASM Build
Setup
Create an index.html next to your Cargo.toml:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link data-trunk rel="rust" />
<style>
body {
margin: 0;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
</html>
Run trunk (defer to Trunk docs for setup):
trunk serve
Running an Android Build
Setup
Make sure your crate is a library:
[lib]
crate-type = ["cdylib", "rlib"]
Have a lib.rs with your egor::main function:
egor::main!(main);
pub fn main() {
App::new().run(...)
}
The egor::main!(main) macro defines the required android_main() entry point on Android and forwards execution to your main() function. On other platforms it expands to nothing (see the secs_particles demo for reference)
Add an Android build target (if needed):
rustup target add aarch64-linux-android
Egor defers Android toolchain setup and build workflow to xbuild. See the xbuild - getting started for SDK/NDK and device setup
Build with xbuild
x build
Try Out Subsecond Hot-reloading
Compile with the hot_reload feature enabled. Hot reload will automatically wrap AppHandler::update when the feature is active
Run dioxus-cli (defer to Dioxus CLI docs for setup):
dx serve --hot-patch
[!NOTE] Subsecond hot-reloading is experimental; native is working
Performance
To stress test egor, we made ferrismark, a bunnymark like demo capable of rendering hundreds of thousands of Ferris crabs at stable FPS on modest hardware
Demos
To try ferrismark or see more of egor in action, check out demos/
Run any demo with (see --help usage) xtask:
cargo xtask run <demo>
Contributing
egor is moving fast. Before opening a PR or submitting a change, please read CONTRIBUTING.md
Check out some issues, open a new one, drop a PR or come hang in Discord
egor is maintained with ❤️ by Open Source Force
Dependencies
~5–26MB
~280K SLoC
