#bevy #ascii #terminal


A simple terminal for rendering ascii in bevy

12 releases (5 breaking)

new 0.6.0 Sep 23, 2021
0.5.6 Sep 8, 2021
0.4.0 Sep 3, 2021
0.3.2 Sep 3, 2021
0.1.0 Sep 1, 2021

#55 in Game dev

Download history 81/week @ 2021-08-29 70/week @ 2021-09-05 1/week @ 2021-09-12 27/week @ 2021-09-19

56 downloads per month

MIT license

1.5K SLoC

A simple ascii terminal integrated into bevy's ecs framework.

What Is It

bevy_ascii_terminal is a utility to easily render colorful ascii in bevy. It was made with "traditional roguelikes" in mind, but should serve effectively as a simple, no fuss UI tool if needed. The API is designed to be as simple and straightforward as possible.

What Can It Do

It can set a single tile or group of tiles to any ascii character that's been mapped, and can set foreground and background colors per tile.

As far as what it can render, it currently supports a fixed size tileset and by default expects a code page 437 layout on the textures, though this can be changed via a configuration file. There are plenty more fonts available around the internet, and changing fonts is as simple as setting a string.


This project is fully integrated with bevy and depends on it for rendering.

While not a direct dependency, I would highly recommend importing bevy_pixel_camera when you use this terminal. Bevy's default camera isn't set up to properly handle scaling up very low resolution pixel art (like terminal glyphs) without noticable artifacts due to pixels being misaligned. All the examples from this crate use bevy_pixel_camera.

Getting Started

Include bevy_ascii_terminal as a dependency in your Cargo.toml, then add the plugin and spawn a bundle with a camera:

fn spawn_terminal(mut commands: Commands) {
    let (w, h) = (80, 25);
    let mut term_bundle = TerminalBundle::with_size(w, h);
    term_bundle.terminal.put_string(1,1, "Hello, world!");

    // 8 is the size of the default terminal font. This setting 
    // will ensure the camera scales up the viewport so the 
    // terminal takes up as much space as possible on the screen
    // given it's current size, while still remaining pixel-perfect
        w as i32 * 8, 
        h as i32 * 8,

fn main() {

And that's it. You can write to the terminal when you create it, or from a query:

fn write_to_terminal(mut q: Query<&mut Terminal>) {
    let term = q.single_mut().unwrap();
    term.put_char(1,1, '');
    term.put_string(2,1, "451");

You can check the examples for more.

Note: For all terminal functions the y coordinate is flipped in the terminal, so put_char(0,0,'x') refers to the top left corner of the terminal, and put_char(width - 1, height - 1, 'x') refers to the bottom right corner. This was done because the terminal is meant to display readable text from left to right, top to bottom.

Changing Fonts

The terminal comes with several fonts built in, or you could find one online or make one yourself. If you add a font you must put it in the "assets/textures" folder. To load a font you just need to modify the TerminalFont component attached to the terminal:

fn change_font(
    keys: Res<Input<KeyCode>>,
    mut q: Query<&mut TerminalRendererFont>,
) {
    if keys.just_pressed(KeyCode::Space) {
        for mut font in q.iter_mut() {
            font.font_name = FONT_TAFFER_10X10.name.to_string();
            font.clip_color = Color::BLACK;

Assuming the name you set is a valid filename from the built in fonts or your own "assets/textures" folder, the terminal will swap to the new font. Notice the clip_color field above - this refers to the "background" color of the texture. The shader will swap this color out for whatever background color you set for a given tile in the terminal. For the included fonts this can remain at the default black. Many fonts you'll find online use magenta as the background color.

Other Features

There are several other components you can modify to alter how the terminal is drawn. To name a few:

Transform: The terminal is a normal bevy entity, you can move and transform it at will.

TerminalTileScaling: Alters how tiles are sized when building the mesh.

TerminalPivot: Determines how the terminal mesh is aligned. Defaults to (0.5,0.5) which will nicely center the terminal mesh regardless of size.

TilePivot: Similar to TerminalPivot but for tiles - this defaults to (0.0,0.0), so a tile's bottom left corner sits on the pivot.


This is my first real rust project - I'm open to any and all feedback and suggestions, particularly with regards to improving the api or project structure. Thanks!


~218K SLoC