Skip to content

Instantly share code, notes, and snippets.

@emilk
Created May 17, 2024 16:09
Show Gist options
  • Save emilk/42843fc4bd369fc71822e36310ef2436 to your computer and use it in GitHub Desktop.
Save emilk/42843fc4bd369fc71822e36310ef2436 to your computer and use it in GitHub Desktop.

egui

One of the most popular GUI libraries for Rust in 2024 is egui.

egui is an immediate mode GUI and aims to be simple, easy to use, and highly portable.

At a glance

fn ui_counter(ui: &mut egui::Ui, counter: &mut i32) {
    ui.horizontal(|ui| {
        if ui.button("−").clicked() {
            *counter -= 1;
        }
        ui.label(counter.to_string());
        if ui.button("+").clicked() {
            *counter += 1;
        }
    });
}

One defining feature of egui is its simplicity and ease-of-use. There are no macros, DSL, messages, or callbacks. A lot of the simplicity is thanks to egui being an immediate mode GUI.

Immediate mode

egui was heavily inspired by the popular C++ library Dear ImGui , and like its predecessor is using the immediate mode GUI paradigm.

Immediate mode has its roots in gaming, where everything on the screen is painted at the display refresh rate, i.e. at 60+ frames per second. In immediate mode GUIs, the entire interface is laid out and painted at the same high rate. This makes immediate mode GUIs especially well suited for highly interactive applications.

To understand what “immediate mode” implies, let's look at an example:

if ui.button("Click me!").clicked() {
    take_action()
}

This code is being executed each frame at maybe 60 frames per second. On each call to ui.button(…) egui does these things:

  • lays out the letters Click me! in order to figure out the size of the button
  • decides where on screen to place the button
  • check if the mouse is hovering or clicking that location
  • chose button colors based on if it is being hovered or clicked
  • add a Shape::Rect and Shape::Text to the list of shapes to be painted later this frame
  • return a Response with the clicked member so the user can check for interactions

There is no button being created and stored somewhere. The only output of this call is some colored shapes, and a Response.

Similarly, consider this code:

ui.add(egui::Slider::new(&mut value, 0.0..=100.0).text("My value"));

Here egui will read value (an f32) to display the slider, then look if the mouse is dragging the slider and if so change the value. Note that egui does not store the slider value for you - it only displays the current value, and changes it by how much the slider has been dragged in the previous few milliseconds. This means it is responsibility of the egui user to store the state (value) so that it persists between frames.

Who uses egui?

egui is very popular as a GUI library for dev-tools that engineers build for themselves and their colleagues. Many Rust shops all over the world use egui for their internal tooling, from individual tinkerers to large teams at tech giants.

egui is less commonly used for user-facing interfaces, but there are at least a few startups doing so, including Rerun.

Integrations

egui was designed to be easy to integrate into existing projects. egui itself doesn't know or care on what OS it is running or how to render things to the screen - that is the job of the egui integration.

An egui integration collects input (mouse, keyboard, screen size, …) and give it to egui. After running the UI code, egui then hands back some output (cursor changes, paste, texture changes, …) and some textured triangle meshes to paint.

egui has been integrated into all popular Rust game engines (bevy, ggez, godot, macroquad, …) and rendering libraries (wgpu, glow, skia, vulkan, …).

egui can also be compiled to Wasm and run in the browser (see egui.rs). In this case egui paints to a WebGL or WebGPU canvas, with no use of the DOM.

You can even run egui in a Wasm executor outside of a web environment, because egui doesn't do any system calls (not even reading the system clock).

eframe is an official egui integration for writing egui application for web and native (Windows, macOS, Linux, Android).

Accessibility

egui and eframe has builtin integration with AccessKit, providing accessibility features for Windows, macOS, and Unix. Notably AccessKit does not yet have an adapter for web, and since egui/eframe does not use the DOM on the web, the accessibility of an egui application on the web is basically zero.

Rendering

egui emits simple shapes (rectangles, text primitives, etc) that is tessellated into textured triangles on the CPU. The tessellator applies feathering (a slight blur of the polygon edges) for anti-aliasing. The result is a few textured triangle meshes and associated clip rectangles (used mostly for scroll-areas).

Text rendering is done with a simple texture atlas of glyphs. Whenever a new glyph is encountered (for a given font, for a given size) it is rasterized into the font atlas. The changed portion of the font atlas is emitted as a texture delta to the egui rendering backend, meaning only the portion of the texture that changed gets uploaded to the GPU. If the font atlas ever gets full, it is cleared. There is no subpixel antialiasing.

This simple approach to the rendering means egui can be integrated anywhere you can rasterize textured triangles, and with minimal effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment