DEV Community

Cover image for Ratatui for Terminal Fireworks: using Rust TUI Canvas
Rodney Lab
Rodney Lab

Posted on • Originally published at rodneylab.com

Ratatui for Terminal Fireworks: using Rust TUI Canvas

🧨 Adding Fireworks to the Ratatui Game

In this Ratatui for Terminal Fireworks post, I talk about how I added fireworks to the Text-based User Interface (TUI) game I created in a recent post. The game is based on the arithmetic challenge from the UK TV quiz, Countdown. The Ratatui game worked already, but was little more than a minimal viable product.

I mentioned some rough corners worth focussing on in that previous post. One of those rough corners was adding some confetti or fireworks to the results screen when the player achieves a perfect score. In this post, I take a quick look at how I added fireworks using the Ratatui canvas. There is a link to the latest project repo, with full code further down.

🧱 Ratatui for Terminal Fireworks: What I Built

The game stayed mostly as it was, and only the victory screen changed, adding a fireworks animation using a Ratatui canvas widget if the player got a perfect score.

Ratatui for Terminal Fireworks: Screen capture shows game running in the Terminal.  The main title reads “How did you do?”.  Below, text reads You nailed it. “You hit the target!”, and below that, taking up more than half the screen, are a number of colourful dots in the shape of a recently ignited firework.

🧑🏽‍🎓 Ratatui Examples

In the post on Trying Ratatui TUI, I listed some resources for getting started with Ratatui, including three official tutorials. Those tutorials focus on text-content, and the Ratatui Canvas widget is a better match for the fireworks animation, as I would want to draw shapes to the window at arbitrary locations.

Luckily, there is another invaluable resource for getting started with Ratatui: the examples in Ratatui’s GitHub repo.

Ratatui for Terminal Fireworks: Canvas examples screen capture show a low-resolution world map in the left half of the terminal, outlined with green dots on a black background.  The right half of the screen shows a further two demos: a pong demo up top, and a collection of rectangles, resembling a histogram at the bottom.  The pong example has a large, yellow ball close to the middle of the screen, while the other demo features red and purple rectangles.

The canvas examples within the repo and more specifically, the Pong frame, within that collection was super helpful in getting going here, as it included example code on timing in Ratatui.

🎆 Fireworks Alternative

There is an alternative for adding fireworks, worth a mention. That combines Ratatui with the Bevy Rust game engine, calling Ratatui from within a Bevy app. This alternative approach uses bevy_ratatui. This lets you take advantage of Bevy features, such as its Entity Component System (ECS) and plugin ecosystem, while still rendering to the Terminal.

At the time of writing, bevy_ratatui is still experimental. Also, I already have a Ratatui app, and wanted to avoid re-writing it with Bevy, so decided to stick with Ratatui’s canvas widget for the firework animation. bevy_ratatui does look promising though, and I will probably try it in another project soon. Let me know if you have already tried it and have some feedback!

🖥️ My Approach using Ratatui Canvas

Creating and drawing to the canvas widget was not too complicated. I created a Rust Vec of firework Sparks structs. Each Spark struct had a colour (selected randomly at initialization) and current position and velocity values. I just needed to loop over that Spark Vec to display each of them on each render.

fn create_result_block_canvas<'a>(app: &'a App, sparks: &'a [Spark]) -> impl Widget + 'a {
    match app.check_solution() {
        Some(0) => Canvas::default()
            .block(Block::default())
            .marker(symbols::Marker::Dot)
            .paint(move |ctx| {
                for Spark {
                    x_position,
                    y_position,
                    colour,
                    ..
                } in sparks
                {
                    ctx.draw(&Circle {
                        x: *x_position,
                        y: *y_position,
                        radius: 1.0,
                        color: *colour,
                    });
                }
            })
            .x_bounds([-100.0, 100.0])
            .y_bounds([-50.0, 50.0]),
        None | Some(_) => Canvas::default(),
    }
}
Enter fullscreen mode Exit fullscreen mode

Ratatui uses immediate mode, so you have to redraw every element for each frame. I updated the position elements for each spark in an on_tick method, which also runs each frames, creating the appearance of moving sparks.

🙌🏽 Ratatui for Terminal Fireworks: Wrapping Up

In this Ratatui for Terminal fireworks post, I briefly ran through how I added fireworks to the Ratatui Countdown game. In particular, we saw:

  • a link to code examples for getting started with Ratatui;
  • the bevy_ratatui app as an alternative route to rendering in the Terminal; and
  • some code snippets and design choices for my own Ratatui game.

I hope you found this useful. As promised, you can get the full project code on the Rodney Lab GitHub repo. I would love to hear from you, if you are also new to Rust game development. Do you have alternative resources you found useful? How will you use this code in your own projects?

🙏🏽 Ratatui for Terminal Fireworks: Feedback

If you have found this post useful, see links below for further related content on this site. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on X, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on X (previously Twitter) and also, join the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Game Dev as well as Rust and C++ (among other topics). Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Top comments (0)