Notcurses
GitHub Overview
dankamongmen/notcurses
blingful character graphics/TUI library. definitely not curses.
Stars4,009
Watchers33
Forks127
Created:November 17, 2019
Language:C
License:Other
Topics
cclincursesterminalterminal-emulators
Star History
Data as of: 7/25/2025, 11:08 AM
Notcurses
Notcurses is a high-performance TUI library that maximizes the use of modern terminal features. It supports graphics, video playback, and multimedia content display, providing expressive capabilities beyond traditional TUI library limitations. It's available as Rust bindings to the C library.
Features
Advanced Graphics Capabilities
- Pixel Manipulation: True pixel-level drawing
- Image Display: Support for JPEG, PNG, GIF, WebP, and other formats
- Video Playback: Video playback within terminals
- Transparency: Alpha channel support
- Blending: Advanced color mixing
Performance Optimization
- Double Buffering: Flicker-free rendering
- Differential Rendering: Only redraw changed portions
- Parallel Processing: Utilize multi-core CPUs
- Memory Efficiency: Optimized memory usage
- Hardware Acceleration: GPU utilization when available
Rich Feature Set
- Full Unicode Support: Emojis, combining characters, RTL languages
- 24-bit Color: True color support
- Mouse Support: High-precision mouse tracking
- Sound: Audio playback capabilities
- Statistics: Rendering statistics and debug information
Terminal Compatibility
- Wide Support: Compatible with major terminal emulators
- Feature Detection: Automatic terminal capability detection
- Graceful Degradation: Fallback for unavailable features
- Sixel/Kitty: Graphics protocol support
Basic Usage
Installation
[dependencies]
notcurses = "3.0"
Basic Initialization and Rendering
use notcurses::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize Notcurses
let mut nc = Notcurses::new()?;
// Get the standard plane
let mut stdplane = nc.stdplane();
// Draw text
stdplane.putstr("Hello, Notcurses!")?;
// Set colors and draw
stdplane.set_fg_rgb(0xFF0000)?; // Red
stdplane.set_bg_rgb(0x000000)?; // Black
stdplane.putstr_yx(2, 0, "Colored text")?;
// Render
nc.render()?;
// Wait for key input
nc.get_blocking(None)?;
Ok(())
}
Plane Manipulation
use notcurses::*;
fn create_window(nc: &mut Notcurses) -> Result<NcPlane, NcError> {
let opts = NcPlaneOptions {
y: 5,
x: 10,
rows: 10,
cols: 40,
userptr: std::ptr::null_mut(),
name: Some("mywindow".to_string()),
resizecb: None,
flags: 0,
margin_b: 0,
margin_r: 0,
};
// Create new plane
let mut plane = NcPlane::new(nc, &opts)?;
// Draw border
plane.perimeter_rounded(0, 0, 0)?;
// Add title
plane.putstr_yx(0, 2, " Window Title ")?;
// Add content
plane.putstr_yx(2, 2, "This is a window!")?;
Ok(plane)
}
Image Display
use notcurses::*;
fn display_image(
nc: &mut Notcurses,
path: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mut stdplane = nc.stdplane();
// Create visual
let mut visual = NcVisual::from_file(path)?;
// Scaling options
let vopts = NcVisualOptions {
n: Some(&mut stdplane),
scaling: NcScale::Scale,
y: 0,
x: 0,
begy: 0,
begx: 0,
leny: 0,
lenx: 0,
blitter: NcBlitter::Default,
flags: 0,
transcolor: 0,
pxoffy: 0,
pxoffx: 0,
};
// Render image
visual.blit(nc, &vopts)?;
nc.render()?;
Ok(())
}
Animation
use notcurses::*;
use std::time::Duration;
use std::thread;
fn animate_text(nc: &mut Notcurses) -> Result<(), NcError> {
let mut plane = nc.stdplane();
let text = "Animated Text";
let max_x = plane.dim_x() as i32 - text.len() as i32;
for x in 0..max_x {
plane.erase();
// Rainbow gradient
let hue = (x as f32 / max_x as f32) * 360.0;
let (r, g, b) = hsl_to_rgb(hue, 1.0, 0.5);
plane.set_fg_rgb8(r, g, b)?;
plane.putstr_yx(5, x, text)?;
nc.render()?;
thread::sleep(Duration::from_millis(50));
}
Ok(())
}
fn hsl_to_rgb(h: f32, s: f32, l: f32) -> (u8, u8, u8) {
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = l - c / 2.0;
let (r, g, b) = match (h / 60.0) as u8 {
0 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x),
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
Advanced Features
Menu System
use notcurses::*;
fn create_menu(nc: &mut Notcurses) -> Result<NcMenu, NcError> {
let sections = vec![
NcMenuSection {
name: "File".to_string(),
items: vec![
NcMenuItem {
desc: "New".to_string(),
shortcut: Some("Ctrl+N".to_string()),
},
NcMenuItem {
desc: "Open".to_string(),
shortcut: Some("Ctrl+O".to_string()),
},
NcMenuItem {
desc: "Save".to_string(),
shortcut: Some("Ctrl+S".to_string()),
},
],
},
NcMenuSection {
name: "Edit".to_string(),
items: vec![
NcMenuItem {
desc: "Copy".to_string(),
shortcut: Some("Ctrl+C".to_string()),
},
NcMenuItem {
desc: "Paste".to_string(),
shortcut: Some("Ctrl+V".to_string()),
},
],
},
];
let opts = NcMenuOptions {
sections,
..Default::default()
};
NcMenu::new(&mut nc.stdplane(), &opts)
}
Graphs and Charts
use notcurses::*;
fn draw_chart(
plane: &mut NcPlane,
data: &[f64],
) -> Result<(), NcError> {
let height = plane.dim_y() as usize;
let width = plane.dim_x() as usize;
// Normalize data
let max_val = data.iter().cloned().fold(f64::MIN, f64::max);
let min_val = data.iter().cloned().fold(f64::MAX, f64::min);
let range = max_val - min_val;
// Draw graph
for (i, &value) in data.iter().enumerate() {
let x = (i * width) / data.len();
let normalized = (value - min_val) / range;
let y = height - (normalized * height as f64) as usize - 1;
// Draw bars
for bar_y in y..height {
plane.putchar_yx(bar_y as i32, x as i32, '█')?;
}
}
Ok(())
}
Ecosystem
Related Projects
- notcurses-rs: Official Rust bindings
- ncplayer: Media player
- ncneofetch: System information display tool
- nctetris: Tetris game implementation
Tools and Utilities
- notcurses-demo: Feature demonstration
- ncls: Enhanced ls command
- ncplayer: Terminal media player
Advantages
- Cutting-edge Features: Full utilization of modern terminal capabilities
- High Performance: Optimized rendering engine
- Rich Expression: Support for images, videos, and graphics
- Professional Quality: Commercial-grade library
- Active Development: Continuous feature additions and improvements
Limitations
- Complexity: Steep learning curve due to rich features
- Dependencies: Depends on C language library
- Terminal Requirements: Latest features require compatible terminals
- Resource Usage: High memory and CPU usage
- Portability: Some features are platform-specific
Comparison with Other Libraries
Feature | Notcurses | Ratatui | Cursive |
---|---|---|---|
Graphics | Full Support | Text Only | Text Only |
Performance | Very High | High | High |
Learning Curve | Very High | Medium | Low-Medium |
Feature Richness | Highest | Medium | Medium |
Portability | Medium | High | High |
Summary
Notcurses is a revolutionary library that maximizes the potential of terminal applications. It enables image display, video playback, and advanced graphics expressions that were impossible with traditional TUI libraries. While the learning curve is steep, it's the optimal choice for developers who want to create professional terminal applications or visually impressive TUIs.