termcanvas π¨
A lightweight Rust library for rendering pixels directly in the terminal. Colors are written into a pixel buffer each frame, which is then flushed to the screen.
Note: Your terminal must support ANSI true color (24-bit). Most modern terminals do β Windows Terminal, iTerm2, Alacritty, Kitty, and WezTerm all work.
Quickstart
let mut canvas = new;
let mut input = new;
let font = load_from_file;
loop
canvas.end;
Features
- πΌοΈ Pixel rendering β set and read individual pixels on a canvas that adapts to your terminal size (details)
- βοΈ Bitmap font system β render text and numbers using two included fonts, with a fluent draw API (details)
- β¨οΈ Input handling β per-frame key-down, key-held, and key-up states via the Kitty keyboard protocol (details)
How it works
Each terminal cell is rendered as two pixels stacked vertically, using the Unicode block character β with an ANSI true-color foreground (top pixel) and background (bottom pixel). A terminal of 80Γ24 cells therefore gives a canvas of 80Γ48 pixels.
To keep rendering fast, color escape codes are only emitted when the color actually changes compared to the previous cell. The entire frame is assembled into a single byte buffer and written in one call, wrapped in synchronized output (?2026h/l) to eliminate flickering.
Usage
Pixel rendering
Colors are passed as 0xRRGGBBAA packed u32 values. You can construct them manually or use the included mathi::rgb_to_u32 helper. Call canvas.clear(color) at the start of each frame to reset the buffer and canvas.render() at the end to flush it to the terminal. When the loop exits, it is recommended to call canvas.end() to restore the terminal to its original state (cursor visibility, alternate screen, etc.).
let mut canvas = new;
let mut input = new;
let red = rgb_to_u32;
let green = rgb_to_u32;
let blue = rgb_to_u32;
loop
canvas.end; // restores cursor, clears alternate screen
Font rendering
Two bitmap fonts are included and ready to use β default and default_bold. Load them from the assets/ folder and use the fluent draw builder to place text at any position and color.
Text is drawn with .text(). For numbers, use .uint(), .int(), or .float() to render values directly without formatting them yourself.
let font = load_from_file;
let font_bold = load_from_file;
let white = rgb_to_u32;
let gray = rgb_to_u32;
canvas.draw.at.color.text;
canvas.draw.at.color.text;
canvas.draw.at.color.uint;
canvas.draw.at.color.text;
canvas.draw.at.color.float; // 1 decimal, no forced sign
canvas.draw.at.color.text;
canvas.draw.at.color.int; // always_show_sign: true -> "+42"
Alignment is set with .align(Align::Left) (default), .align(Align::Right) or .align(Align::Middle). With Align::Left, the x/y coordinates mark the top-left corner of the text. With Align::Right, they mark the top-right corner β useful for right-aligning numbers at a fixed column without calculating their width yourself. With Align::Middle, x/y mark the top-center β useful for centering headings or labels, for example at width / 2 to center across the full canvas.
Input
Call input.update() once at the start of each frame to process all pending events. Key state is tracked across three separate sets, each reset every frame:
| Method | Returns true when⦠|
|---|---|
is_key_down(key) |
The key was pressed this frame (fires once) |
is_key_pressed(key) |
The key is currently held down |
is_key_up(key) |
The key was released this frame (fires once) |
input.update.unwrap;
if input.is_key_down
if input.is_key_pressed
if input.is_key_up
Platform note: On Linux and macOS, the Kitty keyboard protocol is required and must be supported by the terminal for
Inputto initialize. If it is not available, the program will panic on startup β onlyInputis affected, the canvas itself has no such requirement. On Windows, the Kitty protocol is not used and input works out of the box.
Project structure
src/
main.rs # Demo: font specimen, live FPS and canvas size display
canvas.rs # Canvas: pixel buffer, frame render, terminal resize
canvas/
draw.rs # DrawBuilder fluent API, text and number rendering
font.rs # Font: .tcfont file loader and glyph parser
input.rs # Input: raw mode, Kitty protocol, per-frame key state
math.rs # Re-exports mathf, mathi, noise
math/
mathf.rs # Float math helpers
mathi.rs # Integer math helpers
noise.rs # Noise functions
assets/
default.tcfont # Regular bitmap font
default_bold.tcfont # Bold bitmap font