Skip to content

Rust GUI library v0.1

Repaint only
what changed.

Smudge is a native Rust GUI library with a retained widget tree, fine-grained reactive signals and damage-tracked repainting. It is built to stay fast where there is no usable GPU, and it runs on the desktop and in the browser.

$cargo add smudge
mail.rs
41
messages read
Syncing
Last sync12:04:07
frame 1,284 repainted 0.0% of the window 0 rects
24 µs
one small box changes among 600 in a 1920 by 1080 frame
against 3.7 ms to redraw everything
37 µs
one keystroke into a focused text field
against 87 µs for a full redraw
0.31 ms
one scroll step through a thousand mixed rows
the rows keep their paint and only move

Median frame on the CPU renderer, one thread of a Ryzen 7 7700, headless. How these are measured

Repaints only what changed

Every frame works out which pixels a change touched and redraws exactly those. A frame that changes one small box rasterizes and presents one small box.

Fine-grained signals

State lives in signals. A closure that reads a signal binds that one property of that one widget, so a write updates what depends on it and nothing else.

Fast without a GPU

A CPU renderer that is fast on its own, for virtual machines, remote desktops and older laptops. A GPU renderer takes over when a real adapter is there.

A retained widget tree

Widgets are built once and kept. A change that only affects color skips layout, and each node keeps its paint until something it shows changes.

Text and input

Shaped text, a text input with selection, undo and redo, input methods for composing languages, and the system clipboard.

Desktop and the web

One window API for the desktop, tested on Linux in this release, and the CPU renderer in a browser canvas. Desktop builds expose the widget tree to screen readers.

The whole program ​

A button and a label bound to one signal. Each press writes the signal, the label's text closure runs again, and the next frame repaints the label and the pressed button. Nothing else in the window is touched.

rust
use smudge::prelude::*;

fn main() -> Result<(), PlatformError> {
    let count = RwSignal::new(0_u32);
    let press = count.clone();
    let shown = count.clone();

    run_with(
        WindowOptions {
            title: String::from("Counter"),
            size: Size { width: 280.0, height: 140.0 },
            clear_color: Color::rgb(16, 17, 21),
            ..WindowOptions::default()
        },
        move || {
            column()
                .padding(px(24.0))
                .gap(Size { width: 0.0, height: 12.0 })
                .align_items(Align::Center)
                .child(
                    button("Count up")
                        .primary()
                        .on_press(move || press.update(|count| *count += 1)),
                )
                .child(label(move || format!("Clicked {} times", shown.get())))
        },
        |app| app.set_theme(Theme::dark()),
    )
}

Released under the MIT or Apache 2.0 license, at your option.