ggplotcli is a universal converter that renders any
ggplot2 plot in the terminal using Unicode/ASCII characters. It
automatically extracts data, aesthetics, and styling from ggplot
objects, supporting 15+ geom types, faceting, color aesthetics, and
legends.
library(plotcli)
#> Loading required package: R6
#> Loading required package: ggplot2
#> plotcli loaded. Use plotcli_options() to set global options.
library(ggplot2)
# Create a ggplot
p <- ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point() +
labs(title = "MPG vs Weight by Cylinders")
# Render in terminal
ggplotcli(p, width = 60, height = 16)
#>
#> MPG vs Weight by Cylinders
#> 35.0 ⠄
#> ⠐
#> 30.0 ⠐⠐
#> ⠠ fac
#> m 25.0 ⠁ ⢀ * 4
#> p ⠄ ⠄ * 6
#> g 20.0 ⠐ ⠠ ⠐⠠ ⠐ * 8
#> ⠈ ⢰ ⠐
#> ⡀ ⠁ ⠄
#> 15.0 ⠈⠈⠆ ⠈ ⠂
#> ⠁
#> 10.0 ⠂ ⠂
#> 2.0 3.0 4.0 5.0
#> wt
#> p <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
geom_point() +
labs(title = "Iris: Sepal Dimensions by Species")
ggplotcli(p, width = 60, height = 16)
#>
#> Iris: Sepal Dimensions by Species
#> S 4.5 ⠠
#> e ⡀ ⠄
#> p 4.0 ⢀ ⠂
#> a ⢐ ⢀⢀ ⠐ ⠐ ⠂ Species
#> l 3.5 ⠁ ⠁⠨⠠ ⠄ ⠄ ⠁ * setosa
#> . ⢀ ⡁⢀ ⠁ ⢨⠨ ⠁ ⠈ ⡀⠈ ⠁⠨⢀ ⡀ ⠠ ⡀⡀⢀ ⡀ * versicolor
#> W 3.0 ⢀⢀ ⠂ ⡂⡂⢀ ⢀ ⡀⢀ ⡀⢀⢀ ⠐ ⡀⢀⢐ ⡀⠂ ⡀⡀ ⢀⢀ * virginica
#> i ⠐ ⡂⢐ ⡀ ⠐⢐ ⡂⢐⢐ ⡀⠐ ⡀ ⠐⢀ ⢀
#> d 2.5 ⠁ ⠄⠁⠠ ⠅ ⠈⠠ ⠈⠈ ⠠
#> t ⠅ ⠈ ⠅⠁⠈ ⠈ ⠈
#> h ⠁ ⠈ ⠁ ⠐ ⠂⠈
#> 2.0 ⠐
#> 5.0 6.0 7.0 8.0
#> Sepal.Length
#> # Multiple colored lines
df <- data.frame(
x = rep(1:30, 2),
y = c(sin(1:30/4) * 10, cos(1:30/4) * 10),
type = rep(c("sin", "cos"), each = 30)
)
p <- ggplot(df, aes(x, y, color = type)) +
geom_line() +
labs(title = "Sine and Cosine Waves")
ggplotcli(p, width = 60, height = 12)
#>
#> Sine and Cosine Waves
#> 10.0 ⠠⢄⡀ ⣀⠤⠒⠢⠤⣀⡀ ⣀⠤⠔⠒⠤⠤⣀ ⣀⠄
#> ⠈⡱⢖⠉ ⠈⠑⠤⡀ ⣀⠔⠊ ⢉⠶⢎ type
#> 5.0 ⡠⠊ ⠑⠢⡀ ⠈⠢⡀ ⢠⠊ ⣀⠔⠁ ⠣⡀ * cos
#> y ⠈ ⠘⢄ ⠑⢄ ⢀⠔⠁ ⢠⠊ * sin
#> 0 ⠑⢄ ⠑⠤⡀ ⢀⠔⠁ ⢀⠔⠁
#> -5.0 ⠑⠤⡀ ⠈⢆ ⢀⠔⠁ ⡰⠁
#> ⠈⠢⡀ ⡱⠶⣁ ⡠⠒⠉
#> -10.0 ⠈⠑⠒⠒⠤⠤⠒⠉ ⠉⠑⠒⠤⠔⠒⠉
#> 0 5.0 10.0 15.0 20.0 25.0 30.0
#> x
#> p <- ggplot(mtcars, aes(x = mpg, fill = factor(cyl))) +
geom_histogram(bins = 10, position = "dodge") +
labs(title = "MPG Distribution by Cylinders")
ggplotcli(p, width = 60, height = 12)
#>
#> MPG Distribution by Cylinders
#> ⣶⣶
#> c 6.0 ⣿⣿ fac
#> o ⣿⣿ * 4
#> u 4.0 ⣿⣿ ⢰⣶⡆ * 6
#> n ⣿⣿ ⣤⣤⣤⡄⢸⣿⡇⢠⣤⣤ ⣤⣤ * 8
#> t 2.0 ⢠⣤⡄ ⣤⣤ ⣿⣿ ⣿⣿⣿⣧⣼⣿⡇⢸⣿⣿ ⢠⣤⡄ ⣿⣿
#> ⢸⣿⡇ ⣿⣿ ⣿⣿ ⣿⣿⣿⣿⣿⣿⡇⢸⣿⣿ ⢸⣿⡇ ⣿⣿ ⢠⣤⡄
#> 0 ⠠⠤⠤⠼⠿⠧⠤⠤⠿⠿⠤⠤⠤⠿⠿⠤⠿⠿⠿⠿⠿⠿⠧⠼⠿⠿⠤⠤⠼⠿⠧⠤⠤⠤⠤⠤⠤⠤⠿⠿⠤⠤⠼⠿⠧⠤⠤⠄
#> 10.0 15.0 20.0 25.0 30.0 35.0
#> mpg
#> p <- ggplot(mtcars, aes(x = mpg, color = factor(cyl))) +
geom_density() +
labs(title = "MPG Density by Cylinders")
ggplotcli(p, width = 60, height = 12)
#>
#> MPG Density by Cylinders
#> d 0.25 ⣠⢦
#> e 0.20 ⢠⠃⠈⣇ ⢀⡠⠞⠙⣆ fac
#> n ⡞ ⠸⡀ ⢠⠚⠉⠁ ⠸⡄ * 4
#> s 0.15 ⢠⠃ ⢣ ⢠⠃ ⢧ * 6
#> i 0.10 ⢀⡎ ⠈⢆⡎ ⠘⡄ * 8
#> t 0.05 ⠘⢦ ⢀⡞ ⡼⠳⠤⠖⠒⢦ ⢀⣠⠤⠖⢳⠒⠒⠒⠒⠒⠦⠤⠤⣄⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀
#> y ⠈⢣⡀⣠⠞ ⡰⠁ ⢀⣀⡤⠔⢻⡉ ⠈⢧ ⠈⠉⠉⠓⠂
#> 0 ⠠⠤⠤⠭⠥⠤⠤⠤⠴⠶⠒⠚⠓⠒⠉⠉ ⠙⠒⠦⠤⠤⠬⠷⠶⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠄
#> 10.0 15.0 20.0 25.0 30.0 35.0
#> mpg
#> df <- data.frame(
category = c("A", "B", "C", "D", "E", "F"),
value = c(25, 45, 30, 60, 35, 50)
)
p <- ggplot(df, aes(x = category, y = value, fill = category)) +
geom_col() +
labs(title = "Category Values")
ggplotcli(p, width = 60, height = 12)
#>
#> Category Values
#> 60.0 ⣶⣶⣶⣶⣶⣶⣶⣶ cat
#> v 50.0 ⣿⣿⣿⣿⣿⣿⣿⣿ ⣤⣤⣤⣤⣤⣤⣤⣤ * A
#> a 40.0 ⢸⣿⣿⣿⣿⣿⣿⣿⡇ ⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿ * B
#> l 30.0 ⢸⣿⣿⣿⣿⣿⣿⣿⡇⣀⣀⣀⣀⣀⣀⣀⣀⣿⣿⣿⣿⣿⣿⣿⣿⢰⣶⣶⣶⣶⣶⣶⣶⡆⣿⣿⣿⣿⣿⣿⣿⣿ * C
#> u ⣤⣤⣤⣤⣤⣤⣤⣤⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿ * D
#> e 20.0 ⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿ * E
#> 10.0 ⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿ * F
#> 0 ⠿⠿⠿⠿⠿⠿⠿⠿⠸⠿⠿⠿⠿⠿⠿⠿⠇⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠸⠿⠿⠿⠿⠿⠿⠿⠇⠿⠿⠿⠿⠿⠿⠿⠿
#> 1.0 2.0 3.0 4.0 5.0 6.0
#> category
#> Boxplots are rendered with box-drawing characters by default, showing whiskers, box (Q1-Q3), median line, and outliers:
set.seed(42)
df <- data.frame(
group = factor(rep(paste0("Group ", 1:6), each = 50)),
value = c(rnorm(50, 10, 3), rnorm(50, 5, 2), rnorm(50, 8, 4),
rnorm(50, 6, 2), rnorm(50, 12, 3), rnorm(50, 7, 2))
)
p <- ggplot(df, aes(x = group, y = value, fill = group)) +
geom_boxplot() +
labs(title = "Boxplot Colored by Group", y = "Value", x = "Group")
ggplotcli(p, width = 80, height = 20, boxplot_style = "ascii")
#>
#> Boxplot Colored by Group
#> * ───────
#> ─────── │
#> │ * │
#> 15.0 │ ┌───────┐ group
#> │ ───*─── │ │ * Group 1
#> V ┌───────┐ │ │───────│ ─────── * Group 2
#> a │ │ │ └───────┘ │ * Group 3
#> l 10.0 │───────│ ┌───────┐ ─────── │ │ * Group 4
#> u └───────┘ ─────── │ │ ┌───────┐ │ ┌───────┐ * Group 5
#> e │ ┌───────┐ └───────┘ │───────│ ─────── │ │ * Group 6
#> 5.0 │ │───────│ │ └───────┘ └───────┘
#> │ └───────┘ │ │ │
#> ───*─── ─────── │ ─────── ───────
#> ───────
#> 0 * *
#>
#> 1.0 2.0 3.0 4.0 5.0 6.0
#> Group
#> You can also use Braille rendering for boxplots:
ggplotcli(p, width = 80, height = 20, boxplot_style = "braille")
#>
#> Boxplot Colored by Group
#> ⡀
#> ⠒⠒⡖⠒⠂
#> ⠉⠉⡏⠉⠁ ⡇
#> 15.0 ⡇ ⠂ ⡇ group
#> ⡇ ⠂ ⢰⠒⠒⠒⠒⠓⠒⠒⠒⢲ * Group 1
#> V ⢀⣀⣀⣀⣀⣇⣀⣀⣀⡀ ⠉⠉⡏⠉⠁ ⢸ ⢸ ⢀⣀⣀⣀⣀ * Group 2
#> a ⢸ ⡇ ⡇ ⢸⠉⠉⠉⠉⠉⠉⠉⠉⢹ ⢸ * Group 3
#> l 10.0 ⢸⠒⠒⠒⠒⠒⠒⠒⠒⡇ ⡇ ⠐⠒⢲⠒⠒ ⠘⠒⠒⠒⠒⡖⠒⠒⠒⠚ ⢸ * Group 4
#> u ⠸⠤⠤⠤⠤⡤⠤⠤⠤⠇ ⠠⠤⢤⠤⠤ ⢰⠒⠒⠒⠒⠓⠒⠒⠒⢲ ⢸ ⡇ ⢰⠒⠒⠒⠚⠒⠒⠒⠒⡆ * Group 5
#> e ⡇ ⣀⣀⣀⣀⣸⣀⣀⣀⣀⡀ ⢸⠒⠒⠒⠒⠒⠒⠒⠒⢺ ⡖⠒⠒⠒⠚⠒⠒⠒⠒⡆ ⡇ ⢸⠒⠒⠒⠒⠒⠒⠒⠒⡇ * Group 6
#> 5.0 ⡇ ⡗⠒⠒⠒⠒⠒⠒⠒⠒⡇ ⠘⠒⠒⠒⠒⡖⠒⠒⠒⠚ ⣏⣉⣉⣉⣉⣉⣉⣉⣉⡇ ⠉⠉⠉⠉⠁ ⠘⠒⠒⠒⢲⠒⠒⠒⠒⠃
#> ⡇ ⠧⠤⠤⠤⢤⠤⠤⠤⠤⠇ ⡇ ⢸ ⢸
#> ⠤⠤⡧⠤⠄ ⢀⣀⣸⣀⣀ ⡇ ⠠⠤⠼⠤⠤ ⢸
#> ⠒⠒⠓⠒⠂ ⠐⠒⠚⠒⠒
#> 0 ⠄
#> ⠈
#> 1.0 2.0 3.0 4.0 5.0 6.0
#> Group
#> # Histogram with density overlay
p <- ggplot(mtcars, aes(x = mpg)) +
geom_histogram(aes(y = after_stat(density)), bins = 10, fill = "gray") +
geom_density(color = "red") +
labs(title = "Histogram with Density Overlay")
ggplotcli(p, width = 60, height = 12)
#>
#> Histogram with Density Overlay
#> d ⢰⣶⣶⣶⣶⣶
#> e 0.08 ⢸⣿⣿⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣶
#> n 0.06 ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
#> s ⢀⡴⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠓⢦⡀
#> i 0.04 ⣀⠴⠋ ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣤⣤⣭⣶⣤⡀ ⣤⣤⣤⣤⣤
#> t 0.02 ⢠⣤⣤⣤⣾⣥⣤⣤⣤⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣭⣶⣦⣤⣄⣀⣀⣀⣀⣿⣿⣿⣿⣿
#> y ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ ⣿⣿⣿⣿⣿⣽⣶⣤⣤⡄
#> 0 ⠸⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠧⠤⠤⠤⠤⠿⠿⠿⠿⠿⠿⠿⠿⠿⠇
#> 10.0 15.0 20.0 25.0 30.0 35.0
#> mpg
#> # Points with smooth line
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point(color = "gray") +
geom_smooth(method = "loess", color = "red") +
labs(title = "Scatter with LOESS Smooth")
suppressMessages(ggplotcli(p, width = 60, height = 12))
#>
#> Scatter with LOESS Smooth
#>
#> 35.0 ⢐⢤⣀⣀⠁ ⠐
#> m 30.0 ⠉⠲⠢⢄⡀
#> p 25.0 ⠉⠉⡒⠤⣀⣀ ⡐
#> g 20.0 ⠈ ⠐⠉⠩⠙⠒⠒⠢⠬⢄⣀⣠ ⠠
#> 15.0 ⠄ ⢈⠩⡒⠒⢣⠤⠤⢄⣂⣀⣀ ⡀
#> 10.0 ⠁ ⠉⠉⠉⠉⠑⠒⠒⠒⠒⠢⠤⡤⠤⡄
#>
#> 2.0 3.0 4.0 5.0
#> wt
#> Faceting works seamlessly with color/fill aesthetics and legends:
mtcars$cyl_fac <- factor(mtcars$cyl)
p <- ggplot(mtcars, aes(x = wt, y = mpg, color = cyl_fac)) +
geom_point() +
facet_wrap(~cyl_fac) +
labs(title = "MPG vs Weight: Faceted by Cylinders")
ggplotcli(p, width = 75, height = 16)
#>
#> MPG vs Weight: Faceted by Cylinders
#> 4 6 8
#> ⠠
#> ⠁
#> 30.0 ⠉
#> ⠂⡀ cyl
#> ⠄ * 4
#> ⢁ ⡀⠈ ⡀⢀ ⡀ * 6
#> 20.0 ⠄ ⠄ ⡀ ⠄ * 8
#> ⠃ ⠠ ⡀
#> ⠁⢒⠐ ⠄
#> ⠁
#> 10.0 ⠐⠐
#> 2.0 4.0 2.0 4.0 2.0 4.0
#>
#> p <- ggplot(mtcars, aes(x = wt, y = mpg, color = factor(gear))) +
geom_point() +
facet_grid(am ~ cyl) +
labs(title = "MPG: AM (rows) x Cylinders (cols)")
ggplotcli(p, width = 80, height = 20)
#>
#> MPG: AM (rows) x Cylinders (cols)
#> 0, 4 0, 6 0, 8
#>
#> 30.0
#> ⡀
#> 20.0 ⠄ ⠐ ⠄
#> ⠆ ⠂⢀⠂
#> ⠊⠄⡂⠈ ⠂ fac
#> 10.0 ⠠⠠ * 3
#> 1, 4 1, 6 1, 8 * 4
#> ⠐ ⢀ * 5
#> 30.0 ⠒
#> ⠁⠐
#> 20.0 ⠂ ⠠ ⡀⡀
#> ⠈
#> ⠈ ⠂
#> 10.0
#> 2.0 4.0 2.0 4.0 2.0 4.0
#>
#> p <- ggplot(mtcars, aes(x = hp, y = qsec)) +
geom_point(color = "cyan") +
labs(title = "Quarter Mile Time vs HP")
# With border
ggplotcli(p, width = 55, height = 12, border = TRUE)
#>
#> Quarter Mile Time vs HP
#> ⡏⠉⠉⠉⠉⠉⠉⠉⠉⡉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
#> 22.0 ⡇ ⢸
#> q ⡇ ⢀ ⢸
#> s 20.0 ⡇ ⠈⡃ ⠈ ⠐ ⡀ ⢸
#> e 18.0 ⡇ ⠁ ⠁ ⠁ ⠂ ⢠ ⠄⠠ ⢀ ⢸
#> c ⡇ ⠐ ⠸ ⠃ ⠈ ⢸
#> 16.0 ⡇ ⠐ ⠘ ⢀ ⢸
#> ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣈⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
#> 50.0 100 150 200 250 300
#> hp
#>
# With grid
ggplotcli(p, width = 55, height = 12, grid = "major")
#>
#> Quarter Mile Time vs HP
#> ⡇ ⠐⢸ ⡇ ⡇ ⢸ ⡇
#> 22.0 ⠉⠉⡏⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⠉
#> q ⣀⣀⣇⣀⣀⣀⣀⣀⣀⣸⣄⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣸⣀⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣀
#> s 20.0 ⡇ ⡂ ⢸ ⠂⢀ ⡇ ⡇ ⢸ ⡇
#> e 18.0 ⠤⠤⡧⠤⠤⠤⠤⠤⠥⢼⠬⠤⠴⠤⠤⠤⠤⡧⠤⠤⠤⡤⠤⠤⡧⠤⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⠤⠤⠤
#> c ⡇ ⠄⢸ ⡆ ⡇ ⠐⠁ ⡇ ⠈ ⢸ ⡇
#> 16.0 ⠒⠒⡗⠒⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠲⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⣺⠒⠒⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⠒⠒
#> ⡇ ⢸ ⡇ ⡇ ⢸ ⠄ ⡇ ⠐
#> 50.0 100 150 200 250 300
#> hp
#>
# Both
ggplotcli(p, width = 55, height = 12, border = TRUE, grid = "major")
#>
#> Quarter Mile Time vs HP
#> ⡏⠉⢹⠉⠉⠉⠉⠉⠉⡉⡏⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⠉⢹
#> 22.0 ⡧⠤⢼⠤⠤⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⠤⠤⢼
#> q ⡇ ⢸ ⣇ ⡇ ⡇ ⢸ ⢸ ⢸
#> s 20.0 ⡏⠉⢹⠉⠉⡋⠉⠉⠉⠉⡏⠙⠉⡉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⡏⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⢹⠉⠉⠉⠉⠉⠉⠉⢹
#> e 18.0 ⡧⠤⢼⠥⠤⠤⠤⠤⠤⠥⡧⠥⠤⠦⠤⠤⠤⡧⠤⠤⠤⢤⠤⠤⡧⠤⠤⠤⢤⠤⠤⢼⠤⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⠤⠤⢼
#> c ⣇⣀⣸⣀⣀⣀⣀⣀⣐⣀⣇⣸⣀⣀⣀⣀⣀⣇⣀⣀⣈⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣸⣀⣀⣀⣀⣀⣀⣸⣀⣀⣀⣀⣀⣀⣀⣸
#> 16.0 ⡇ ⢸ ⡇ ⡇ ⠐ ⡇ ⠘⢸ ⢸ ⢀ ⢸
#> ⣇⣀⣸⣀⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣇⣀⣀⣀⣀⣀⣀⣸⣀⣈⣀⣀⣀⣀⣸⣀⣀⣀⣀⣀⣀⣀⣸
#> 50.0 100 150 200 250 300
#> hp
#> ggplotcli automatically respects ggplot2 themes:
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point(color = "blue") +
labs(title = "theme_bw() - Grid + Border")
# theme_bw has both grid and border
ggplotcli(p + theme_bw(), width = 60, height = 14, border = "auto", grid = "auto")
#>
#> theme_bw() - Grid + Border
#> 35.0 ⣏⣉⣉⣏⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣏⣉⣹
#> ⡧⠤⠤⡧⠤⠤⠤⠥⠤⡧⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⠤⡧⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⢼⠤⠤⠤⠤⠤⡧⠤⢼
#> 30.0 ⡗⠒⠒⡟⠚⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⡗⠒⢺
#> m ⡯⠭⠭⡯⠭⠭⠭⠭⠯⡯⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⡯⠭⠭⠭⠭⠭⡯⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⡯⠭⢽
#> p 25.0 ⡗⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⢒⠒⢺⠒⠒⠒⠒⠒⢺⠒⡚⠒⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⡗⠒⢺
#> g 20.0 ⡯⠭⠭⡯⠭⠭⠭⠭⠭⡯⠭⠭⠭⠭⢽⠭⠯⢭⠯⠯⢽⠭⠭⠯⠭⢭⡯⠭⠭⠭⡭⠭⡯⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⢽⠭⠭⠭⠭⠭⡯⠭⢽
#> ⡗⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠚⡗⠒⠒⠖⠒⠒⣗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⡗⠒⢺
#> 15.0 ⣏⣉⣉⣏⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣉⣙⣟⣏⣉⣙⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣩⣉⣏⣉⣹
#> ⡗⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⠒⡗⠒⠒⠒⠒⢺⠒⠒⠒⠒⠒⢺⠒⠒⡒⠒⢒⡗⠒⢺
#> 10.0 ⣏⣉⣉⣏⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣉⣏⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣹⣉⣉⣉⣉⣉⣏⣉⣹
#> 2.0 3.0 4.0 5.0
#> wt
#>
# theme_classic has border but no grid
ggplotcli(p + theme_classic() + labs(title = "theme_classic() - Border Only"),
width = 60, height = 14, border = "auto", grid = "auto")
#>
#> theme_classic() - Border Only
#> 35.0 ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
#> ⡇ ⠁ ⠄ ⢸
#> 30.0 ⡇ ⠈⠈ ⢸
#> m ⡇ ⠂ ⠠ ⢸
#> p 25.0 ⡇ ⢀ ⡈ ⢸
#> g 20.0 ⡇ ⠁ ⠂⢀⠂⠂ ⠂ ⢀ ⡀ ⢸
#> ⡇ ⠘ ⠄ ⢀ ⢸
#> 15.0 ⡇ ⠈ ⠐⠐⠆ ⠐⡀ ⠠ ⢸
#> ⡇ ⡀ ⢀ ⢸
#> 10.0 ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
#> 2.0 3.0 4.0 5.0
#> wt
#> Three rendering modes are available:
wave <- ggplot(data.frame(x = 1:25, y = sin(1:25/4)*8), aes(x, y)) +
geom_line(color = "green")
# Braille (highest resolution - 2x4 dots per character)
ggplotcli(wave + labs(title = "Braille Canvas (highest resolution)"),
width = 50, height = 8, canvas_type = "braille")
#>
#> Braille Canvas (highest resolution)
#> ⢀⣀⠤⠔⠒⠒⠒⠒⠒⠒⠤⢄⡀
#> y 5.0 ⠔⠊⠁ ⠈⠑⠤⣀
#> -5.0 ⠉⠑⠢⢄⡀ ⣀⠤⠒⠉
#> ⠈⠑⠢⠤⠤⠤⠤⠤⠤⠒⠊⠉
#> 0 5.0 10.0 15.0 20.0 25.0
#> x
#>
# Block (medium resolution - uses block characters)
ggplotcli(wave + labs(title = "Block Canvas (medium resolution)"),
width = 50, height = 8, canvas_type = "block")
#>
#> Block Canvas (medium resolution)
#> ▄▄▄▀▀▀▀▀▄▄▄
#> y 5.0 ▄▀▀ ▀▀▀▄▄
#> -5.0 ▀▄▄▄ ▄▄▀▀
#> ▀▀▀▄▄▄▄▄▀▀▀
#> 0 5.0 10.0 15.0 20.0 25.0
#> x
#>
# ASCII (basic, most compatible)
ggplotcli(wave + labs(title = "ASCII Canvas (most compatible)"),
width = 50, height = 8, canvas_type = "ascii")
#>
#> ASCII Canvas (most compatible)
#> *********
#> y 5.0 ***** *****
#> -5.0 **** ******
#> *********
#> 0 5.0 10.0 15.0 20.0 25.0
#> x
#> ggplotcli intelligently maps ggplot2 colors to terminal colors (red, yellow, green, cyan, blue, magenta). The mapping prioritizes color distinctness over hue accuracy to maximize visual differentiation between groups.
Even with many groups, colors are distributed to minimize repetition:
set.seed(123)
df <- data.frame(
group = factor(rep(paste0("G", 1:8), each = 30)),
value = unlist(lapply(1:8, function(i) rnorm(30, mean = i * 2, sd = 1.5)))
)
p <- ggplot(df, aes(x = group, y = value, fill = group)) +
geom_boxplot() +
labs(title = "8 Groups with Optimized Color Distribution")
ggplotcli(p, width = 85, height = 18, boxplot_style = "ascii")
#>
#> 8 Groups with Optimized Color Distribution
#> ─────
#> * ───── ┌─────┐ grou
#> * ┌─────┐ └─────┘ * G1
#> 15.0 ───── │─────│ ───── * G2
#> v ───── ┌─────┐ └─────┘ * G3
#> a ───── ┌─────┐ └─────┘ * G4
#> l 10.0 * ┌─────┐ │─────│ * G5
#> u ───── ───── └─────┘ └─────┘ * G6
#> e │ └─────┘ ───── * G7
#> 5.0 ───── ┌─────┐ ───── * G8
#> ┌─────┐ └─────┘ *
#> └─────┘ ─────
#> 0 ─────
#>
#> 2.0 4.0 6.0 8.0
#> group
#> set.seed(42)
df <- data.frame(
treatment = factor(rep(c("Control", "Drug A", "Drug B"), each = 60)),
timepoint = factor(rep(rep(c("Baseline", "Week 4", "Week 8"), each = 20), 3)),
response = c(
rnorm(20, 50, 10), rnorm(20, 52, 10), rnorm(20, 51, 10), # Control
rnorm(20, 50, 10), rnorm(20, 65, 12), rnorm(20, 70, 11), # Drug A
rnorm(20, 50, 10), rnorm(20, 58, 11), rnorm(20, 62, 10) # Drug B
)
)
p <- ggplot(df, aes(x = timepoint, y = response)) +
geom_boxplot(aes(fill = treatment)) +
labs(title = "Treatment Response Over Time",
subtitle = "Faceted by treatment group",
x = "Timepoint", y = "Response") +
theme_bw()
ggplotcli(p, width = 100, height = 22, boxplot_style = "ascii")
#>
#> Treatment Response Over Time
#> Faceted by treatment group
#> ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
#> 100 ⡇ ⢸
#> ⡇ * ⢸
#> ⡇ ───── * ⢸
#> R 80.0 ⡇ │ ───── ───── ⢸
#> e ⡇ ───── ┌─────┐ * │ │ ⢸ treatment
#> s ⡇ │ ───── │ │ * ───── ┌─────┐┌─────┐ ⢸ * Control
#> p ⡇ ┌─────┐ ───── ───── │ │─────│ ───── │ └─────┘│─────│ ⢸ * Drug A
#> o 60.0 ⡇ │ │┌─────┐ │ ┌─────┐└─────┘┌─────┐ ┌─────┐ │ └─────┘ ⢸ * Drug B
#> n ⡇ │─────││─────│┌─────┐ │ │ │ └─────┘ │─────│ ──*── │ ⢸
#> s ⡇ └─────┘│ ││─────│ │─────│ ───── │ └─────┘ ───── ⢸
#> e ⡇ │ └─────┘└─────┘ └─────┘ ──*── │ * ⢸
#> 40.0 ⡇ │ ───── │ │ ───── ⢸
#> ⡇ │ ───── │ ⢸
#> ⡇ ───── ───── ⢸
#> 20.0 ⡇ * ⢸
#> ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
#> 0.50 1.0 1.5 2.0 2.5 3.0 3.5
#> Timepoint
#> # Simulated experiment data
set.seed(123)
x <- seq(0, 10, length.out = 50)
df <- data.frame(
x = rep(x, 3),
y = c(
2 * x + rnorm(50, 0, 1.5),
1.5 * x + 3 + rnorm(50, 0, 1.2),
x^1.2 + rnorm(50, 0, 1)
),
group = rep(c("Linear", "Offset", "Power"), each = 50)
)
p <- ggplot(df, aes(x = x, y = y, color = group)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE) +
labs(title = "Multi-Group Regression Analysis",
subtitle = "Points with LOESS smoothing",
x = "Predictor", y = "Response") +
theme_minimal()
suppressMessages(ggplotcli(p, width = 70, height = 18))
#>
#> Multi-Group Regression Analysis
#> Points with LOESS smoothing
#> ⠄ ⠠
#> 20.0 ⠂⢀⡐⣤⢰⣾
#> R ⡀⣠⡄⣶⢰⣇⣿⣿⣿⣿⣿
#> e 15.0 ⢀⢀⠠⡀⣤⣠⣾⣸⣯⣿⣿⣿⣿⣿⣿⡟⣿⢹⠿ group
#> s ⠂⡀⡀⣬⢰⣾⣸⣧⣿⣾⣿⣿⣿⣿⡟⣿⠹⠇⠛⢁⠁ ⠈ * Linear
#> p ⢀ ⡀ ⠐⠐⣀⣠⢠⣦⣿⣷⣿⣿⣿⢿⡟⣿⢻⠿⠸⠃⠋⠡ ⠂⠁ * Offset
#> o 10.0 ⠠ ⡀⣠⢠⣴⢴⡇⣿⣷⣿⣿⡟⣿⡏⡿⢸⠛⠘⠁⡁ ⠈ * Power
#> n ⡀⣥⡆⣶⢰⣿⢸⡇⣿⣸⣿⣿⡿⣿⡯⠿⠘⠃⠉ ⠐
#> s ⡀ ⠐ ⢄⣀⢤⡆⣶⣖⣿⢸⡇⣿⣇⣿⣼⣿⢾⡟⡿⡹⠟⠘⠑⠁ ⠈
#> e 5.0 ⢀⡀⣠⢠⣴⢰⡇⣿⢸⣿⢸⡇⣿⣧⣿⣾⡿⣿⡟⡿⠹⠫⠈ ⠂
#> ⣿⡇⣿⢸⣿⣸⣿⣿⢾⣿⢻⡟⠿⠃⠛⠈⠁
#> 0 ⣿⡷⣿⠿⢿⠙⠣⠋⡊⠁ ⠈
#> ⠋
#> 0 2.0 4.0 6.0 8.0 10.0
#> Predictor
#> p <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Length, color = Species)) +
geom_point() +
facet_wrap(~Species) +
labs(title = "Iris Dataset: Sepal vs Petal Length",
subtitle = "Faceted by species with color legend") +
theme_bw()
ggplotcli(p, width = 80, height = 16)
#>
#> Iris Dataset: Sepal vs Petal Length
#> setosa versicolor virginica
#> ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹ ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹ ⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
#> ⡇ ⢸ ⡇ ⢸ ⡇ ⢀ ⠜⢀ ⢸
#> 6.0⡇ ⢸ ⡇ ⢸ ⡇ ⠐⠠⢠⢄⠠⠆⠂⠐ ⢸
#> ⡇ ⢸ ⡇ ⢀ ⢀ ⢸ ⡇ ⣀⣀⠁⢊⢫⠨⢑ ⢸ Species
#> ⡇ ⢸ ⡇ ⠠⡠⠄⠡⠆⣜⡤⣔⠉⠂ ⢸ ⡇ ⠄ ⠈ ⠈⠁⠉ ⢸ * setosa
#> 4.0⡇ ⢸ ⡇ ⠠ ⡾⠳⠑⠂ ⢸ ⡇ ⢸ * versicolor
#> ⡇ ⢸ ⡇ ⠚⡀ ⠈⠁ ⢸ ⡇ ⢸ * virginica
#> ⡇ ⢸ ⡇ ⢸ ⡇ ⢸
#> 2.0⡇ ⠠ ⡄⢀ ⡀ ⢸ ⡇ ⢸ ⡇ ⢸
#> ⡇ ⡐⢚⠛⠻⠛⠙⠂⠡ ⢸ ⡇ ⢸ ⡇ ⢸
#> ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸ ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸ ⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
#> 5.0 6.0 7.0 8.0 5.0 6.0 7.0 8.0 5.0 6.0 7.0 8.0
#>
#> | Feature | Status |
|---|---|
| geom_point | ✓ |
| geom_line, geom_path | ✓ |
| geom_bar, geom_col, geom_histogram | ✓ |
| geom_density | ✓ |
| geom_smooth | ✓ |
| geom_area | ✓ |
| geom_segment, geom_hline, geom_vline | ✓ |
| geom_rect | ✓ |
| geom_text | ✓ |
| geom_boxplot | ✓ |
| Color/fill aesthetics | ✓ |
| Legends (auto-generated) | ✓ |
| facet_wrap, facet_grid | ✓ |
| Titles, subtitles, captions | ✓ |
| Theme auto-detection | ✓ |
| Optimized color mapping | ✓ |
| Multiple canvas types | ✓ |
| geom_tile (heatmaps) | Planned |
| geom_violin | Planned |