Canvas
⚠️ Experimental Feature Canvas requires the Kitty graphics protocol and only works in compatible terminals (Kitty, Ghostty, WezTerm). Other terminals will show a placeholder message.
Pixel-level drawing using the Kitty graphics protocol for charts, graphs, and visualizations.
View::canvas()
.width(200)
.height(100)
.on_draw(|ctx| {
ctx.clear(Color::Black);
ctx.line(0, 0, 200, 100, Color::Red);
ctx.fill_rect(50, 50, 100, 40, Color::Blue);
ctx.circle(150, 50, 20, Color::Green);
})
.build()
Run with: cargo run -p telex-tui --example 29_canvas
Terminal compatibility
⚠️ Important: Canvas requires terminals that support the Kitty graphics protocol. Check compatibility before using canvas widgets.
✅ Supported terminals:
- Kitty
- Ghostty
- WezTerm (with Kitty protocol enabled)
❌ Not supported:
- iTerm2 (uses different protocol)
- Alacritty (no inline graphics)
- Standard terminal.app
If your terminal doesn't support Kitty graphics, canvas widgets won't render. No fallback is provided - use a compatible terminal or stick to character-based visualizations.
Basic canvas
Create a canvas with width and height in pixels:
View::canvas()
.width(400) // pixels wide
.height(300) // pixels tall
.on_draw(|ctx| {
// Drawing code here
})
.build()
The on_draw closure receives a drawing context with primitive operations.
Drawing primitives
Clear
Fill the entire canvas with a color:
ctx.clear(Color::Rgb { r: 30, g: 30, b: 40 });
Lines
Draw lines between two points:
ctx.line(x1, y1, x2, y2, color);
// Example: horizontal line
ctx.line(0, 50, 400, 50, Color::Red);
// Example: diagonal
ctx.line(0, 0, 400, 300, Color::Blue);
Rectangles
Draw filled rectangles:
ctx.fill_rect(x, y, width, height, color);
// Example: red square at (100, 100)
ctx.fill_rect(100, 100, 50, 50, Color::Red);
Circles
Draw circles (filled):
ctx.circle(center_x, center_y, radius, color);
// Example: green circle
ctx.circle(200, 150, 30, Color::Green);
Coordinate system
- Origin
(0, 0)is top-left - X increases to the right
- Y increases downward
- Units are pixels
Colors
Use Color enum for drawing:
Color::Red
Color::Blue
Color::Green
Color::Cyan
Color::Magenta
Color::Yellow
Color::White
Color::Black
Color::Rgb { r: 100, g: 150, b: 200 }
Animation
Combine canvas with streams for animated graphics:
let frame = stream!(cx, || {
(0u32..).map(|i| {
std::thread::sleep(Duration::from_millis(50));
i
})
});
View::canvas()
.width(200)
.height(100)
.on_draw({
let current_frame = frame.get();
move |ctx| {
ctx.clear(Color::Black);
// Animate position
let x = (current_frame % 200) as i32;
ctx.circle(x, 50, 10, Color::Red);
}
})
.build()
Each frame triggers a re-render with updated position.
Bar charts
Visualize data with bar charts:
let data = vec![30, 50, 40, 80, 60];
View::canvas()
.width(200)
.height(100)
.on_draw(move |ctx| {
ctx.clear(Color::Black);
let bar_width = 200 / data.len() as i32;
for (i, &value) in data.iter().enumerate() {
let x = i as i32 * bar_width;
let height = value as i32;
let y = 100 - height;
ctx.fill_rect(x, y, bar_width - 2, height, Color::Cyan);
}
})
.build()
Line graphs
Plot data points with lines:
let points = vec![(0, 50), (50, 30), (100, 70), (150, 40), (200, 60)];
View::canvas()
.width(200)
.height(100)
.on_draw(move |ctx| {
ctx.clear(Color::Black);
for i in 0..points.len() - 1 {
let (x1, y1) = points[i];
let (x2, y2) = points[i + 1];
ctx.line(x1, y1, x2, y2, Color::Green);
}
})
.build()
Performance
Canvas operations are efficient - redrawing 60 times per second is feasible for simple graphics.
For complex scenes:
- Limit drawing operations
- Cache static elements
- Use lower frame rates
Real-world uses
System monitoring:
let cpu = cpu_usage(); // 0-100
ctx.fill_rect(0, 0, cpu * 2, 20, Color::Green);
Progress visualization:
let progress = 0.7; // 70%
let width = (400.0 * progress) as i32;
ctx.fill_rect(0, 0, width, 30, Color::Blue);
Sparklines:
let history = vec![10, 15, 13, 18, 22, 20, 25];
for (i, &v) in history.iter().enumerate() {
let x = i as i32 * 5;
let y = 30 - v;
ctx.fill_rect(x, y, 4, v, Color::Cyan);
}
Tips
Check terminal compatibility - Test in Kitty/Ghostty/WezTerm. Canvas won't work in all terminals.
Pixel coordinates - Canvas uses pixel units, not character cells.
Clear first - Always call ctx.clear() at the start of your draw function to avoid artifacts.
Keep it simple - Complex graphics can be slow. Test performance if animating.
Alternative: ASCII art - For terminal-agnostic apps, use character-based visualizations instead of canvas.
Size matters - Larger canvases use more resources. Don't make canvases bigger than needed.
No text rendering - Canvas doesn't support text. Use regular TUI widgets for labels.
Next: Keyed State