Ply: A Rust UI Engine That Actually Makes Sense

Building apps in Rust is supposed to be empowering. You get memory safety, performance, and the satisfaction of knowing your code won't randomly crash. But try building a GUI and you'll discover that Rust's ecosystem has a problem: every UI framework makes simple things needlessly complicated.

Ply fixes that. It's a new cross-platform app engine that cuts through the noise and gives you what you actually want — an immediate-mode UI that doesn't fight you at every step.

Why Rust GUI Development Was Broken

If you've tried building a GUI in Rust, you know the pain. Bevy forces you into an ECS architecture that turns button clicks into database queries. Iced drowns every line in ..default() and .into() calls. egui makes you manually call .add_space() for basic layouts. Slint looks clean but forces you into a separate markup language that doesn't belong in your Rust codebase.

The author of Ply went through this exact journey trying to build a multiplayer board game. He wanted one language, clean architecture, and full control. Instead, he got complexity that made simple tasks take forever and architectural decisions that broke his server integration plans.

So he built what he wished existed: a framework that makes the common case simple without sacrificing power.

What Makes Ply Different

Ply is immediate-mode, which means you rebuild your UI every frame. This sounds expensive but isn't — layout takes a fraction of a percent of frame time, and you have to redraw anyway. What you gain is complete control over what renders and when, no mutation tracking, and no fighting with ownership rules.

The API uses builder patterns with closures. No more .end() calls everywhere or deep indentation nightmares:

use ply_engine::prelude::*;

div()
    .background_color(0x1a1a1a)
    .padding(20)
    .child(|| {
        text("Hello World")
            .color(0xffffff)
            .size(24)
    });

Everything accepts Into<T> generics. When .background_color() takes Into<Color>, you can pass hex integers, float tuples, or existing color objects. When .image() takes Into<ImageSource>, it handles file paths, embedded bytes, textures, or vector graphics. No wrapper functions.

Memory management happens through global managers (TEXTURE_MANAGER, FONT_MANAGER, NET_MANAGER). Tell the engine where your assets are, it handles caching and lifetime. You never think about loading the same image twice or cleaning up textures between frames.

Installation and First Steps

cargo install plyx
plyx init

Two commands get you a working app with Google Fonts integration, feature flags, and a proper project structure. The generated code is clean — no boilerplate ceremony, just what you need to start building.

The interactive docs are worth mentioning. Instead of static examples, you get live code playgrounds where you can change values and see results instantly. Every concept has hands-on examples that teach by doing, not lecturing.

The Complete Feature Set

Ply ships with everything missing from other Rust frameworks:

  • Layout engine: Flexbox-like sizing, padding, gaps, alignment, scrolling, floating elements
  • Real text input: Cursor, selection, undo/redo, multiline, password mode, all keyboard shortcuts
  • Rich text styling: Inline colors, wave effects, gradients, typewriter animation, shadows
  • GLSL shaders: Apply to any element, with built-in effects and SPIR-V pipeline
  • Accessibility: AccessKit on desktop, JavaScript bridge on web
  • Debug tools: Chrome DevTools-style inspector (no other Rust UI library has this)
  • Networking: HTTP and WebSocket support that never blocks the UI
  • Cross-platform: Linux, macOS, Windows, Android, iOS, web — all from one codebase

The shader support deserves special attention. You can apply custom GLSL effects to any UI element, not just game sprites. Want a button with a custom distortion effect? Just chain .shader("my_effect.glsl") and it works.

The Honest Assessment

Ply is still young. The 1.0 release happened recently, which means you're dealing with a smaller ecosystem compared to mature frameworks. Documentation is excellent for what exists, but you might hit edge cases that need GitHub issues rather than Stack Overflow answers.

The immediate-mode approach also shifts how you think about state. Instead of mutating UI components, you rebuild them based on application state. This is cleaner long-term but requires unlearning imperative UI patterns if you're coming from traditional frameworks.

Cross-platform support is impressive but may have platform-specific quirks. The web target works through WebAssembly, which adds a build step and size considerations for deployment.

Performance is good for most applications, but if you're building something that needs 120fps with thousands of UI elements, you'll want to profile carefully. Immediate-mode rebuilding every frame is fast, but "fast enough" depends on your specific use case.

Who This Is Actually For

Ply shines for developers who want to build real applications in Rust without fighting the GUI framework. If you've tried other Rust UI libraries and gotten frustrated with boilerplate or architectural constraints, this is worth your time.

It's particularly strong for games, creative tools, and applications where you need custom rendering or effects. The shader support and immediate-mode control give you flexibility that traditional widget frameworks can't match.

Avoid it if you need mature widget libraries or if you're building standard business applications where framework quirks matter less than ecosystem maturity. For those use cases, web frameworks or native platform tools might be more practical.

Go Try It

Start with the interactive docs and play with the live examples. The shader playground and snake game show what's possible beyond basic UI.

The source lives at github.com/TheRedDeveloper/ply-engine under Zero-Clause BSD license — use it for anything, no attribution required.

Rust finally has a UI framework that doesn't make you choose between simplicity and power. That's worth building something with.