High-performance terminal plotting for Rust.
txtplot renders mathematical plots, 3D visualizations, games, and complex terminal interfaces using Unicode Braille and block characters plus ANSI colors.
Unlike many TUI plotting libraries, txtplot is designed for speed: it uses flat contiguous buffers, renderer-specific cell encodings, clipping, and a zero-allocation rendering path to support real-time terminal graphics.
License: MIT.
This repository carries a small set of foundational documents for users, contributors, and agents:
- AGENTS.md - implementation guidance for AI agents and contributors
- CONSTITUTION.md - project principles, decision hierarchy, and compatibility posture
- ARCHITECTURE.md - module boundaries, data flow, and extension seams
- GUIDE.md - user guide for plots, 3D visuals, games, and terminal interfaces
- CONFIGURATION.md - toolchain, shell, and API-level configuration surfaces
- RELEASE.md - release checklist and versioning process
If you use Nix, nix develop provides the Rust toolchain plus cargo-nextest, just, keel, and sift.
- High resolution: 8 sub-pixels per character (Braille 2x4). A 100x50 terminal yields a 200x200 effective pixel canvas.
- Pluggable cell renderers: Braille (
2x4), HalfBlock (1x2), and Quadrant (2x2) - Performance-oriented design:
- flat buffers for cache-friendly access
- zero-allocation rendering via
render_to - Cohen-Sutherland line clipping to discard off-screen geometry before rasterization
- Advanced pixel and color control:
unset_pixelandtoggle_pixel- color blending modes with
OverwriteandKeepFirst - cell background colors for terminal panels and HUD-style layouts
- styled text with foreground, background, plus
Normal/Bold/Dimintensity - cell-space UI helpers with
text_screen(),text_screen_styled(),label_screen(), andpanel_screen()
- High-level chart aesthetics:
legend(): automatic, opaque legend boxes with color markersanchored_text(): effortless label placement atTopLeft,Center, etc.- opaque UI panels that clear underlying raster data for professional overlays
- Drawing primitives:
- lines, circles, polygons
- filled shapes via
rect_filledandcircle_filled - text overlay support
- Ready-to-use charts:
scatter(),line_chart(),bar_chart(),pie_chart(),plot_function()- linear and
log10axes throughAxisScale
- Auto-range and axis helpers for chart setup
- Exported 3D building blocks in
txtplot::three_d:Vec3,Projection, andZBuffer- screen projection helpers plus z-buffered raster helpers
- sphere, torus, and triangle mesh generators
Add this to your Cargo.toml:
[dependencies]
txtplot = "0.1.0"
colored = "2.0"use colored::Color;
use txtplot::ChartContext;
fn main() {
let mut chart = ChartContext::new(60, 15);
chart.draw_grid(10, 4, Some(Color::BrightBlack));
chart.draw_axes((0.0, 10.0), (-1.5, 1.5), Some(Color::White));
chart.plot_function(|x: f64| x.sin(), 0.0, 10.0, Some(Color::Cyan));
chart.plot_function(
|x: f64| (x * 0.5).cos() * 0.5,
0.0,
10.0,
Some(Color::Magenta),
);
chart.text("sin(x)", 0.75, 0.85, Some(Color::Cyan));
chart.text("0.5*cos(0.5x)", 0.56, 0.10, Some(Color::Magenta));
println!("{}", chart.canvas.render());
}Use text_styled() or the screen-space styled text helpers when you need label emphasis without changing the label content:
use colored::Color;
use txtplot::{ChartContext, TextStyle};
fn main() {
let mut chart = ChartContext::new(40, 10);
chart.text_styled(
"core-node",
0.45,
0.75,
TextStyle::new().with_foreground(Color::BrightWhite).bold(),
);
chart.text_styled(
"outer-node",
0.55,
0.30,
TextStyle::new().with_foreground(Color::BrightBlack).dim(),
);
println!("{}", chart.canvas.render());
}Place labels and legends effortlessly using ChartAnchor without calculating pixel offsets:
use colored::Color;
use txtplot::{ChartAnchor, ChartContext, TextStyle};
fn main() {
let mut chart = ChartContext::new(60, 15);
chart.plot_function(|x| x.sin(), 0.0, 10.0, Some(Color::Cyan));
// Place a legend in the top right
let entries = [("Sine Wave", TextStyle::new().with_foreground(Color::Cyan))];
chart.legend(ChartAnchor::TopRight, &entries);
// Anchor a title or status label
chart.anchored_text_styled(
"LIVE MONITOR",
ChartAnchor::TopLeft,
TextStyle::new().with_foreground(Color::Yellow).bold(),
);
println!("{}", chart.canvas.render());
}use colored::Color;
use txtplot::{AxisScale, ChartContext};
fn main() {
let mut chart = ChartContext::new(60, 15);
chart.set_scales(AxisScale::Linear, AxisScale::Log10);
let points = vec![(1.0, 1.0), (2.0, 10.0), (3.0, 100.0), (4.0, 1000.0)];
let (range_x, range_y) = ChartContext::get_auto_range_scaled(
&points,
0.05,
AxisScale::Linear,
AxisScale::Log10,
);
chart.draw_axes(range_x, range_y, Some(Color::White));
chart.line_chart(&points, Some(Color::Cyan));
println!("{}", chart.canvas.render());
}If you are building a real-time app, avoid render() and use render_to():
use std::fmt;
use colored::Color;
use txtplot::ChartContext;
fn main() -> fmt::Result {
let mut chart = ChartContext::new(60, 15);
chart.draw_axes((0.0, 10.0), (-1.0, 1.0), Some(Color::White));
chart.plot_function(|x: f64| x.sin(), 0.0, 10.0, Some(Color::Cyan));
let mut buffer = String::with_capacity(8000);
chart.canvas.render_to(&mut buffer, true, Some("60 FPS UI"))?;
print!("{buffer}");
Ok(())
}Braille remains the default renderer, and HalfBlockCanvas / HalfBlockChartContext plus QuadrantCanvas / QuadrantChartContext provide alternate encodings. The showcase example renders the same chart and raster scene through all three renderers:
cargo run --release --example renderer_showcaseIf you want to choose a renderer from CLI or config input while still constructing a concrete generic chart or canvas, use RendererKind together with with_renderer!:
cargo run --release --example runtime_renderer -- halfblocktxtplot does not ship a full scene graph, but it now exports reusable 3D math, projection, and z-buffer helpers under txtplot::three_d. The surface example projects a volatility mesh into terminal pixels, uses a z-buffer for occlusion, and overlays a gradient-ascent path:
cargo run --release --example vol_surfaceThe examples also include a small self-contained manifold-learning demo: it builds clustered 6D data, runs a lightweight t-SNE embedding, and overlays the original-space 3-nearest-neighbor graph on top of the 2D layout.
cargo run --release --example tsne_neighborsTo avoid mathematical confusion, txtplot offers two coordinate modes and multiple pixel operators:
| Coordinate Mode | Origin (0,0) | Y Direction | Best For |
|---|---|---|---|
| Cartesian | Bottom-left | Grows up | Math plots, functions, charts |
| Screen | Top-left | Grows down | UI, games, sprites, 3D projections |
Pixel manipulation methods:
set_pixel / set_pixel_screen: turn a dot onunset_pixel / unset_pixel_screen: turn a dot offtoggle_pixel_screen: flip the current state of a dot
Cell-space HUD methods:
text_screen: write text in top-left screen coordinateslabel_screen: write text with per-cell background stylingpanel_screen: draw boxed UI regions for dashboards and overlays
The following block is captured from cargo run --example demo using the built-in render_no_color path:
A) Plain Render (render_no_color):
⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀
⠀⠀⠈⠉⠢⢄⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⡠⠔⠉⠁⠀⠀
⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠈⢆⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⡠⠃⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⢀⡠⠔⠉⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
The repository includes advanced examples and demos:
- Function and chart gallery
cargo run --example demo- Primitive shapes and blending
cargo run --release --example primitives_demo- 3D volatility surface with gradient ascent
cargo run --release --example vol_surface- Braille, half-block, and quadrant renderer showcase
cargo run --release --example renderer_showcase- Runtime renderer selection from CLI input
cargo run --release --example runtime_renderer -- quadrant- t-SNE embedding with nearest-neighbor graph
cargo run --release --example tsne_neighbors- 3D gallery with camera, z-buffer, and zoom
cargo run --release --example 3dengine- Solar system Kepler 3D
cargo run --release --example solarsystem_kepler- Sprite engine
cargo run --release --example sprite_demo- Interactive fractals
cargo run --release --example fractalmovetxtplot is optimized for real-time terminal rendering.
In a benchmark with a 236x104 sub-pixel canvas filled with trigonometric noise and particles on a modern machine:
- Debug mode: about 60 FPS
- Release mode: about 1600+ FPS
Use just bench or cargo bench --bench canvas_benchmark to run the current Criterion suite. It now includes renderer comparison groups for chart-heavy and raster-heavy scenes across Braille, HalfBlock, and Quadrant output.
Detailed implementation planning now lives in ARCHITECTURE.md. The list below is only the user-facing summary.
- Flat contiguous buffers for memory efficiency
- Explicit coordinate APIs for screen and cartesian modes
- Cohen-Sutherland line clipping
- Zero-allocation rendering via
render_to - Filled primitives and erasers
- Color blending policies
- Logarithmic scaling support
- Automatic legend box
- Trait-based pluggable terminal renderers
- Runtime renderer selection helpers
This project is licensed under the MIT License.