The main entry point for creating plots.
import { gg } from '@ggterm/core'
// Create a plot from data
const plot = gg(data)
.aes({ x: 'var1', y: 'var2' })
.geom(geom_point())| Method | Description |
|---|---|
gg(data) |
Create a new plot with the given data |
.aes(mapping) |
Set aesthetic mappings |
.geom(geom) |
Add a geometry layer |
.stat(stat) |
Add a statistical transformation |
.scale(scale) |
Add or modify a scale |
.coord(coord) |
Set the coordinate system |
.facet(facet) |
Add faceting |
.theme(theme) |
Apply a theme |
.labs(labels) |
Set labels (title, x, y, etc.) |
.render(options) |
Render to string |
.push(data) |
Add streaming data points |
interface AestheticMapping {
x: string // Required: x-axis variable
y: string // Required: y-axis variable
color?: string // Point/line color
fill?: string // Fill color (bars, areas)
size?: string // Point size
shape?: string // Point shape
alpha?: string // Transparency
group?: string // Grouping variable
label?: string // Text labels
}Renders data points as scatter plot markers.
import { geom_point } from '@ggterm/core'
// Basic scatter plot
gg(data).aes({ x: 'pc1', y: 'pc2' }).geom(geom_point())
// With options
gg(data).aes({ x: 'pc1', y: 'pc2' }).geom(geom_point({
size: 2,
shape: 'circle', // 'circle' | 'square' | 'triangle' | 'cross'
alpha: 0.8
}))| Option | Type | Default | Description |
|---|---|---|---|
size |
number | 1 | Point size multiplier |
shape |
string | 'circle' | Point shape |
alpha |
number | 1 | Opacity (0-1) |
color |
string | null | Fixed color (overrides aes) |
Connects data points with lines.
import { geom_line } from '@ggterm/core'
gg(data).aes({ x: 'time', y: 'value', group: 'series' }).geom(geom_line())
// With options
gg(data).aes({ x: 'time', y: 'value' }).geom(geom_line({
linewidth: 1,
linetype: 'solid' // 'solid' | 'dashed' | 'dotted'
}))| Option | Type | Default | Description |
|---|---|---|---|
linewidth |
number | 1 | Line thickness |
linetype |
string | 'solid' | Line style |
alpha |
number | 1 | Opacity |
Connects data points in the order they appear in the data (unlike geom_line which sorts by x).
import { geom_path } from '@ggterm/core'
gg(data).aes({ x: 'longitude', y: 'latitude' }).geom(geom_path())| Option | Type | Default | Description |
|---|---|---|---|
linewidth |
number | 1 | Line thickness |
linetype |
string | 'solid' | Line style |
alpha |
number | 1 | Opacity |
Connects data points with step functions (horizontal then vertical).
import { geom_step } from '@ggterm/core'
gg(data).aes({ x: 'time', y: 'value' }).geom(geom_step())
// Control step direction
gg(data).aes({ x: 'time', y: 'value' }).geom(geom_step({ direction: 'vh' }))| Option | Type | Default | Description |
|---|---|---|---|
direction |
string | 'hv' | Step direction: 'hv' (horizontal-vertical) or 'vh' |
linewidth |
number | 1 | Line thickness |
alpha |
number | 1 | Opacity |
Renders vertical bars.
import { geom_bar } from '@ggterm/core'
// Count occurrences (stat = 'count')
gg(data).aes({ x: 'category' }).geom(geom_bar())
// Pre-computed heights (stat = 'identity')
gg(data).aes({ x: 'category', y: 'value' }).geom(geom_bar({ stat: 'identity' }))| Option | Type | Default | Description |
|---|---|---|---|
stat |
string | 'count' | Statistical transformation |
width |
number | 0.9 | Bar width (0-1) |
position |
string | 'stack' | Position adjustment |
Renders bars with heights from data (shorthand for geom_bar({ stat: 'identity' })).
import { geom_col } from '@ggterm/core'
gg(data).aes({ x: 'category', y: 'value' }).geom(geom_col())| Option | Type | Default | Description |
|---|---|---|---|
width |
number | 0.9 | Bar width (0-1) |
position |
string | 'stack' | Position adjustment |
Renders histogram bars showing distribution of a single variable.
import { geom_histogram } from '@ggterm/core'
gg(data).aes({ x: 'value' }).geom(geom_histogram())
// Control bin count
gg(data).aes({ x: 'value' }).geom(geom_histogram({ bins: 30 }))| Option | Type | Default | Description |
|---|---|---|---|
bins |
number | 30 | Number of bins |
binwidth |
number | null | Width of each bin (overrides bins) |
position |
string | 'stack' | Position adjustment |
Renders frequency polygons (line-based histograms).
import { geom_freqpoly } from '@ggterm/core'
gg(data).aes({ x: 'value', color: 'group' }).geom(geom_freqpoly())| Option | Type | Default | Description |
|---|---|---|---|
bins |
number | 30 | Number of bins |
binwidth |
number | null | Width of each bin |
Renders filled areas under lines.
import { geom_area } from '@ggterm/core'
gg(data).aes({ x: 'time', y: 'value' }).geom(geom_area({
alpha: 0.5
}))Renders a filled area between ymin and ymax values.
import { geom_ribbon } from '@ggterm/core'
gg(data).aes({ x: 'time', ymin: 'lower', ymax: 'upper' }).geom(geom_ribbon({
alpha: 0.3
}))| Option | Type | Default | Description |
|---|---|---|---|
alpha |
number | 0.5 | Fill opacity |
fill |
string | null | Fixed fill color |
Adds text labels to the plot.
import { geom_text } from '@ggterm/core'
gg(data).aes({ x: 'pc1', y: 'pc2', label: 'name' }).geom(geom_text({
nudge_x: 0.5,
nudge_y: 0.5
}))Adds text labels with background rectangles.
import { geom_label } from '@ggterm/core'
gg(data).aes({ x: 'x', y: 'y', label: 'name' }).geom(geom_label({
padding: 0.2
}))| Option | Type | Default | Description |
|---|---|---|---|
padding |
number | 0.1 | Padding around text |
nudge_x |
number | 0 | Horizontal offset |
nudge_y |
number | 0 | Vertical offset |
Adds reference lines.
import { geom_hline, geom_vline } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.geom(geom_hline({ yintercept: 0, linetype: 'dashed' }))
.geom(geom_vline({ xintercept: 0, linetype: 'dashed' }))Bins continuous data for histograms.
import { stat_bin } from '@ggterm/core'
gg(data)
.aes({ x: 'value' })
.stat(stat_bin({ bins: 30 }))
.geom(geom_bar({ stat: 'identity' }))| Option | Type | Default | Description |
|---|---|---|---|
bins |
number | 30 | Number of bins |
binwidth |
number | null | Bin width (overrides bins) |
center |
number | null | Center of a bin |
Adds smoothed conditional means.
import { stat_smooth } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.stat(stat_smooth({ method: 'loess', span: 0.75 }))| Option | Type | Default | Description |
|---|---|---|---|
method |
string | 'loess' | Smoothing method ('loess', 'lm') |
span |
number | 0.75 | LOESS span |
se |
boolean | true | Show standard error band |
Computes kernel density estimates.
import { stat_density } from '@ggterm/core'
gg(data)
.aes({ x: 'value' })
.stat(stat_density({ kernel: 'gaussian' }))
.geom(geom_area())Computes 2D kernel density estimates for contour plots.
import { stat_density_2d } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.stat(stat_density_2d({ n: 25, h: 1.5 }))
.geom(geom_contour())| Option | Type | Default | Description |
|---|---|---|---|
n |
number | 25 | Grid resolution (n × n points) |
nx |
number | n | Grid resolution for x-axis |
ny |
number | n | Grid resolution for y-axis |
h |
number | [number, number] | auto | Bandwidth (uses Scott's rule if not specified) |
adjust |
number | 1 | Bandwidth adjustment multiplier |
The stat computes a density grid using Gaussian kernel density estimation and Scott's rule for automatic bandwidth selection: h = n^(-1/6) × σ.
Summarizes data by groups.
import { stat_summary } from '@ggterm/core'
gg(data)
.aes({ x: 'group', y: 'value' })
.stat(stat_summary({ fun: 'mean', fun.min: 'min', fun.max: 'max' }))import {
scale_x_continuous,
scale_y_continuous,
scale_x_log10,
scale_y_log10,
scale_x_discrete,
scale_y_discrete
} from '@ggterm/core'
// Set limits and breaks
gg(data).scale(scale_x_continuous({
limits: [0, 100],
breaks: [0, 25, 50, 75, 100],
labels: ['0%', '25%', '50%', '75%', '100%']
}))
// Log scale
gg(data).scale(scale_x_log10())| Option | Type | Default | Description |
|---|---|---|---|
limits |
[number, number] | auto | Scale limits |
breaks |
number[] | auto | Tick positions |
labels |
string[] | auto | Tick labels |
trans |
string | 'identity' | Transformation |
expand |
[number, number] | [0.05, 0] | Expansion factor |
import {
scale_color_continuous,
scale_color_discrete,
scale_color_viridis,
scale_color_manual
} from '@ggterm/core'
// Viridis palette (great for colorblind accessibility)
gg(data).scale(scale_color_viridis())
// Manual colors
gg(data).scale(scale_color_manual({
values: { 'group_a': '#e41a1c', 'group_b': '#377eb8' }
}))
// Built-in palettes
gg(data).scale(scale_color_discrete({ palette: 'Set1' }))Sequential: viridis, plasma, inferno, magma, cividis Diverging: RdBu, BrBG, PiYG, PRGn Categorical: category10, Set1, Set2, Set3, Dark2, Paired
import { coord_cartesian, coord_flip, coord_polar } from '@ggterm/core'
// Default Cartesian
gg(data).coord(coord_cartesian())
// Flip x and y
gg(data).coord(coord_flip())
// Polar (limited terminal support)
gg(data).coord(coord_polar({ theta: 'x' }))| Option | Type | Default | Description |
|---|---|---|---|
xlim |
[number, number] | null | X-axis limits (zoom only) |
ylim |
[number, number] | null | Y-axis limits (zoom only) |
clip |
boolean | true | Clip to plot area |
Creates small multiples wrapped into rows.
import { facet_wrap } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.facet(facet_wrap('category', { ncol: 3 }))| Option | Type | Default | Description |
|---|---|---|---|
ncol |
number | auto | Number of columns |
nrow |
number | auto | Number of rows |
scales |
string | 'fixed' | Scale sharing: 'fixed', 'free', 'free_x', 'free_y' |
Creates a grid of facets.
import { facet_grid } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.facet(facet_grid({ rows: 'var1', cols: 'var2' }))import { theme_minimal, theme_dark, theme_classic, theme_void } from '@ggterm/core'
gg(data).theme(theme_minimal())
gg(data).theme(theme_dark())import { theme } from '@ggterm/core'
gg(data).theme(theme({
panel: {
background: '#1e1e1e',
border: 'rounded',
grid: { major: '#333', minor: null }
},
axis: {
text: { color: '#ccc' },
ticks: { char: '┼' },
title: { bold: true }
},
legend: {
position: 'right'
}
}))interface Theme {
panel: {
background: string
border: 'none' | 'single' | 'double' | 'rounded'
grid: { major: string | null, minor: string | null }
}
axis: {
text: { color: string, size: number }
ticks: { char: string, length: number }
title: { color: string, bold: boolean }
}
legend: {
position: 'right' | 'bottom' | 'none'
title: { bold: boolean }
}
title: {
align: 'left' | 'center' | 'right'
bold: boolean
}
}gg(data).labs({
title: 'Main Title',
subtitle: 'Subtitle',
caption: 'Data source',
x: 'X Axis Label',
y: 'Y Axis Label',
color: 'Color Legend',
fill: 'Fill Legend'
})Renders the plot to a string.
const output = plot.render({
width: 80, // Characters wide
height: 24, // Characters tall
renderer: 'braille' // 'braille' | 'block' | 'sixel' | 'auto'
})
console.log(output)| Option | Type | Default | Description |
|---|---|---|---|
width |
number | 80 | Output width in characters |
height |
number | 24 | Output height in characters |
renderer |
string | 'auto' | Renderer type |
colorMode |
string | 'auto' | Color mode ('none', '16', '256', 'truecolor') |
Box and whisker plots for distribution summaries.
import { geom_boxplot } from '@ggterm/core'
gg(data)
.aes({ x: 'group', y: 'value' })
.geom(geom_boxplot())Violin plots showing distribution shape.
import { geom_violin } from '@ggterm/core'
gg(data)
.aes({ x: 'group', y: 'value' })
.geom(geom_violin({ width: 0.8 }))| Option | Type | Default | Description |
|---|---|---|---|
width |
number | 0.8 | Violin width |
draw_quantiles |
number[] | [0.25, 0.5, 0.75] | Quantile lines to draw |
scale |
string | 'area' | 'area', 'count', or 'width' |
trim |
boolean | true | Trim to data range |
1D kernel density estimation - a smoothed version of the histogram.
import { geom_density } from '@ggterm/core'
// Basic density plot
gg(data)
.aes({ x: 'value' })
.geom(geom_density())
// Compare distributions with color
gg(data)
.aes({ x: 'value', color: 'group', fill: 'group' })
.geom(geom_density({ alpha: 0.3 }))| Option | Type | Default | Description |
|---|---|---|---|
n |
number | 512 | Number of evaluation points |
bw |
number | auto | Bandwidth (Silverman's rule if not specified) |
kernel |
string | 'gaussian' | Kernel: 'gaussian', 'epanechnikov', 'rectangular' |
adjust |
number | 1 | Bandwidth adjustment factor |
alpha |
number | 0.3 | Fill transparency |
color |
string | - | Outline color |
fill |
string | - | Fill color |
linetype |
string | 'solid' | Line type for outline |
Rectangular tiles for heatmaps.
import { geom_tile, scale_fill_viridis } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', fill: 'value' })
.geom(geom_tile({ width: 1, height: 1 }))
.scale(scale_fill_viridis())| Option | Type | Default | Description |
|---|---|---|---|
width |
number | auto | Tile width |
height |
number | auto | Tile height |
alpha |
number | 1 | Opacity |
Contour lines for 2D density.
import { geom_contour } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', z: 'z' })
.geom(geom_contour({ bins: 10 }))Filled contour regions for 2D density.
import { geom_contour_filled } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', z: 'z' })
.geom(geom_contour_filled({ bins: 10 }))2D kernel density estimation shown as contours.
import { geom_density_2d } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_density_2d())2D binned heatmap.
import { geom_bin2d } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_bin2d({ bins: [20, 20] }))| Option | Type | Default | Description |
|---|---|---|---|
bins |
number[] | [30, 30] | Number of bins [x, y] |
Fast rendering of regular grids (similar to tile but optimized).
import { geom_raster } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', fill: 'value' })
.geom(geom_raster())Adds a smoothed conditional mean line.
import { geom_smooth } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.geom(geom_smooth({ method: 'loess' }))| Option | Type | Default | Description |
|---|---|---|---|
method |
string | 'loess' | Smoothing method: 'loess', 'lm', 'gam' |
span |
number | 0.75 | Smoothing span (for loess) |
se |
boolean | true | Show confidence interval |
Adds marginal rug marks showing data positions.
import { geom_rug } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.geom(geom_rug({ sides: 'bl' }))| Option | Type | Default | Description |
|---|---|---|---|
sides |
string | 'bl' | Sides for rugs: 'b', 't', 'l', 'r' combinations |
length |
number | 0.03 | Rug mark length |
Line segments defined by start and end points.
import { geom_segment } from '@ggterm/core'
gg(data)
.aes({ x: 'x1', y: 'y1', xend: 'x2', yend: 'y2' })
.geom(geom_segment())Curved line segments.
import { geom_curve } from '@ggterm/core'
gg(data)
.aes({ x: 'x1', y: 'y1', xend: 'x2', yend: 'y2' })
.geom(geom_curve({ curvature: 0.3 }))| Option | Type | Default | Description |
|---|---|---|---|
curvature |
number | 0.5 | Curvature amount |
angle |
number | 90 | Curve angle |
Error bars for uncertainty visualization.
import { geom_errorbar, geom_point } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', ymin: 'lower', ymax: 'upper' })
.geom(geom_errorbar({ width: 0.2 }))
.geom(geom_point())| Option | Type | Default | Description |
|---|---|---|---|
width |
number | 0.5 | Cap width |
alpha |
number | 1 | Opacity |
Crossbar showing median with box range.
import { geom_crossbar } from '@ggterm/core'
gg(data)
.aes({ x: 'group', y: 'median', ymin: 'lower', ymax: 'upper' })
.geom(geom_crossbar({ width: 0.5 }))Vertical line ranges (no horizontal caps).
import { geom_linerange } from '@ggterm/core'
gg(data)
.aes({ x: 'x', ymin: 'lower', ymax: 'upper' })
.geom(geom_linerange())Point with vertical range line.
import { geom_pointrange } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y', ymin: 'lower', ymax: 'upper' })
.geom(geom_pointrange())Rectangular regions.
import { geom_rect } from '@ggterm/core'
gg(data)
.aes({ xmin: 'x1', xmax: 'x2', ymin: 'y1', ymax: 'y2' })
.geom(geom_rect({ alpha: 0.3 }))Arbitrary lines (y = slope * x + intercept).
import { geom_abline } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.geom(geom_abline({ slope: 1, intercept: 0, linetype: 'dashed' }))Ridgeline plots (also called joy plots) for comparing distributions across groups. Shows stacked density curves, ideal for visualizing how distributions change across categories.
import { geom_ridgeline, geom_joy } from '@ggterm/core'
// Temperature distributions by month
gg(data)
.aes({ x: 'temp', y: 'month' })
.geom(geom_ridgeline())
.labs({ title: 'Temperature by Month' })
// Using the joy alias (same as ridgeline)
gg(data)
.aes({ x: 'value', y: 'category' })
.geom(geom_joy({ scale: 1.2, alpha: 0.7 }))| Option | Type | Default | Description |
|---|---|---|---|
scale |
number | 0.9 | Height scale for density curves (higher = more overlap) |
alpha |
number | 0.8 | Opacity (0-1) |
fill |
string | null | Fill color (cycles through palette if not set) |
color |
string | null | Outline color |
adjust |
number | 1 | Bandwidth adjustment for density estimation |
n |
number | 128 | Number of points for density curve |
outline |
boolean | true | Whether to draw outlines |
# Basic ridgeline plot
npx ggterm-plot data.csv temp month - "Temperatures" ridgeline
# Using joy alias
npx ggterm-plot data.csv value category - "Distribution" joyQ-Q (quantile-quantile) plots for comparing distributions, typically to check for normality.
import { geom_qq, geom_qq_line } from '@ggterm/core'
// Check if data is normally distributed
gg(data)
.aes({ sample: 'value' })
.geom(geom_qq())
.geom(geom_qq_line())| Option | Type | Default | Description |
|---|---|---|---|
distribution |
string | 'norm' | Reference distribution |
dparams |
object | {} | Distribution parameters |
# Q-Q plot for normality check
npx ggterm-plot data.csv value - - "Normality Check" qqBeeswarm plots arrange points to avoid overlap while showing individual data points. Combines the benefits of jitter plots and violin plots.
import { geom_beeswarm, geom_quasirandom } from '@ggterm/core'
// Basic beeswarm plot
gg(data)
.aes({ x: 'group', y: 'value' })
.geom(geom_beeswarm())
.labs({ title: 'Treatment Response' })
// Using quasirandom alias (uses center method)
gg(data)
.aes({ x: 'treatment', y: 'response', color: 'treatment' })
.geom(geom_quasirandom())| Option | Type | Default | Description |
|---|---|---|---|
method |
string | 'swarm' | Arrangement method: 'swarm' (compact), 'center' (alternating), 'square' (grid) |
size |
number | 1 | Point size multiplier |
cex |
number | 1 | Point size for collision detection |
alpha |
number | 1 | Opacity (0-1) |
color |
string | null | Fixed point color |
shape |
string | 'circle' | Point shape |
side |
number | 0 | Side to place points: 0 (both), -1 (left), 1 (right) |
priority |
string | 'ascending' | Order to place points: 'ascending', 'descending', 'density', 'random' |
dodge |
number | 0.8 | Width for categorical spacing |
# Basic beeswarm plot
npx ggterm-plot data.csv group value - "Comparison" beeswarm
# Using quasirandom alias
npx ggterm-plot data.csv treatment response - "Response" quasirandomDumbbell charts show two points connected by a line. Perfect for before/after comparisons, paired data, or showing ranges.
import { geom_dumbbell } from '@ggterm/core'
// Before/after comparison
const data = [
{ category: 'A', before: 10, xend: 25 },
{ category: 'B', before: 15, xend: 20 },
{ category: 'C', before: 8, xend: 30 },
]
gg(data)
.aes({ x: 'before', y: 'category' })
.geom(geom_dumbbell())
.labs({ title: 'Before vs After' })| Option | Type | Default | Description |
|---|---|---|---|
size |
number | 2 | Size of start points |
sizeEnd |
number | size | Size of end points |
color |
string | null | Color of start points |
colorEnd |
string | color | Color of end points |
lineColor |
string | '#666666' | Color of connecting line |
lineWidth |
number | 1 | Width of connecting line |
alpha |
number | 1 | Opacity (0-1) |
shape |
string | 'circle' | Point shape |
# Dumbbell plot with xend in data
npx ggterm-plot data.csv start category - "Comparison" dumbbellLollipop charts show a line from baseline to a point with a dot at the end. A cleaner alternative to bar charts, especially for sparse data.
import { geom_lollipop } from '@ggterm/core'
// Product sales comparison
const data = [
{ product: 'Apple', sales: 150 },
{ product: 'Banana', sales: 200 },
{ product: 'Cherry', sales: 80 },
]
gg(data)
.aes({ x: 'product', y: 'sales' })
.geom(geom_lollipop())
.labs({ title: 'Product Sales' })| Option | Type | Default | Description |
|---|---|---|---|
size |
number | 2 | Size of the point |
color |
string | null | Color of the point |
lineColor |
string | null | Color of the line (defaults to dimmer point color) |
lineWidth |
number | 1 | Width of the line |
alpha |
number | 1 | Opacity (0-1) |
shape |
string | 'circle' | Point shape |
direction |
string | 'vertical' | 'vertical' or 'horizontal' |
baseline |
number | 0 | Baseline value for the lines |
# Vertical lollipop
npx ggterm-plot data.csv category value - "Sales" lollipop
# With custom baseline
npx ggterm-plot data.csv product profit - "Profit vs Target" lollipopThese geoms are specifically designed for terminal display using Unicode characters.
Grid-based part-of-whole visualization. A more readable alternative to pie charts.
import { geom_waffle } from '@ggterm/core'
const data = [
{ company: 'Apple', share: 45 },
{ company: 'Samsung', share: 30 },
{ company: 'Other', share: 25 },
]
gg(data)
.aes({ fill: 'company', y: 'share' })
.geom(geom_waffle())
.labs({ title: 'Market Share' })| Option | Type | Default | Description |
|---|---|---|---|
rows |
number | 10 | Number of rows in the grid |
cols |
number | 10 | Number of columns in the grid |
n_total |
number | 100 | Total units to represent |
fill_char |
string | '█' | Character for filled cells |
empty_char |
string | '░' | Character for empty cells |
show_legend |
boolean | true | Show category legend |
flip |
boolean | false | Fill by row instead of column |
gap |
number | 0 | Gap between cells |
Inline mini-charts showing trends. Uses Unicode block characters for compact visualization.
import { geom_sparkline, SPARK_BARS } from '@ggterm/core'
const data = [
{ month: 1, value: 10 },
{ month: 2, value: 15 },
{ month: 3, value: 12 },
// ...
]
gg(data)
.aes({ x: 'month', y: 'value' })
.geom(geom_sparkline())| Option | Type | Default | Description |
|---|---|---|---|
type |
string | 'bar' | Sparkline type: 'bar', 'line', 'dot' |
width |
number | 20 | Width in characters |
height |
number | 1 | Height (for line type) |
show_minmax |
boolean | false | Highlight min/max points |
color |
string | null | Sparkline color |
min_color |
string | '#e74c3c' | Minimum point color |
max_color |
string | '#2ecc71' | Maximum point color |
normalize |
boolean | true | Normalize to 0-1 range |
import { SPARK_BARS, SPARK_DOTS } from '@ggterm/core'
// Bar characters: ▁▂▃▄▅▆▇█
SPARK_BARS // ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']
// Dot characters for smoother lines
SPARK_DOTS // ['⠀', '⢀', '⢠', '⢰', '⢸', '⣸', '⣾', '⣿']Bullet charts - compact progress bars with target markers. Stephen Few's alternative to gauges.
import { geom_bullet } from '@ggterm/core'
const data = [
{ metric: 'Revenue', value: 85, target: 90 },
{ metric: 'Profit', value: 70, target: 80 },
{ metric: 'Growth', value: 95, target: 75 },
]
gg(data)
.aes({ x: 'metric', y: 'value' })
.geom(geom_bullet())
.labs({ title: 'KPI Dashboard' })| Option | Type | Default | Description |
|---|---|---|---|
width |
number | 40 | Width of chart in characters |
height |
number | 1 | Height per bullet |
target_char |
string | '│' | Target marker character |
bar_char |
string | '█' | Bar character |
range_chars |
array | ['░','▒','▓'] | Background range characters |
show_values |
boolean | true | Show numeric values |
target_color |
string | '#e74c3c' | Target marker color |
orientation |
string | 'horizontal' | 'horizontal' or 'vertical' |
High-resolution plots using Unicode Braille patterns. Each character contains a 2x4 grid of dots for 8x the resolution.
import { geom_braille, BRAILLE_BASE, BRAILLE_DOTS } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_braille({ type: 'line' }))
.labs({ title: 'High-Resolution Plot' })| Option | Type | Default | Description |
|---|---|---|---|
type |
string | 'point' | Plot type: 'point' or 'line' |
color |
string | null | Dot/line color |
fill |
boolean | false | Fill area under line |
alpha |
number | 1 | Opacity (0-1) |
dot_size |
number | 1 | Dots per data point (1-4) |
import { BRAILLE_BASE, BRAILLE_DOTS } from '@ggterm/core'
// Base Unicode point for braille
BRAILLE_BASE // 0x2800
// Dot bit positions: [col][row]
BRAILLE_DOTS // [[0x01,0x02,0x04,0x40], [0x08,0x10,0x20,0x80]]GitHub-style contribution heatmap showing activity over time. Data should have date and value fields.
import { geom_calendar } from '@ggterm/core'
const data = [
{ date: '2025-01-01', commits: 5 },
{ date: '2025-01-02', commits: 3 },
// ...
]
gg(data)
.aes({ x: 'date', fill: 'commits' })
.geom(geom_calendar())
.labs({ title: 'Contribution Activity' })| Option | Type | Default | Description |
|---|---|---|---|
cell_char |
string | '█' | Character for cells |
empty_char |
string | '░' | Character for empty cells |
empty_color |
string | '#161b22' | Color for empty/zero cells |
fill_color |
string | '#39d353' | Color for max value |
show_months |
boolean | true | Show month labels |
show_days |
boolean | true | Show day-of-week labels |
week_start |
number | 0 | Week start day (0=Sun, 1=Mon) |
levels |
number | 5 | Number of color intensity levels |
Flame graph for performance profiling. Displays hierarchical stack data as horizontal bars where width represents time/samples.
import { geom_flame } from '@ggterm/core'
const data = [
{ name: 'main', depth: 0, value: 100, start: 0 },
{ name: 'foo', depth: 1, value: 40, start: 0 },
{ name: 'bar', depth: 1, value: 30, start: 40 },
// ...
]
gg(data)
.aes({ x: 'name', y: 'depth', fill: 'value' })
.geom(geom_flame())
.labs({ title: 'CPU Profile' })| Option | Type | Default | Description |
|---|---|---|---|
style |
string | 'flame' | 'flame' (bottom-up) or 'icicle' (top-down) |
palette |
string | 'warm' | Color palette: 'warm', 'cool', 'hot' |
show_labels |
boolean | true | Show function names in bars |
min_label_width |
number | 10 | Minimum bar width to show label |
sort |
string | 'alpha' | Sort frames: 'alpha', 'value', 'none' |
bar_char |
string | '█' | Character for bars |
geom_icicle()- Same asgeom_flame({ style: 'icicle' })
Correlation matrix heatmap with diverging colors. Displays pairwise correlations between variables.
import { geom_corrmat } from '@ggterm/core'
const data = [
{ var1: 'A', var2: 'A', correlation: 1.0 },
{ var1: 'A', var2: 'B', correlation: 0.8 },
{ var1: 'A', var2: 'C', correlation: -0.3 },
// ... full matrix
]
gg(data)
.aes({ x: 'var1', y: 'var2', fill: 'correlation' })
.geom(geom_corrmat())
.labs({ title: 'Correlation Matrix' })| Option | Type | Default | Description |
|---|---|---|---|
show_values |
boolean | true | Show correlation values in cells |
decimals |
number | 2 | Decimal places for values |
show_significance |
boolean | false | Show significance markers |
sig_threshold |
number | 0.05 | Significance threshold |
sig_marker |
string | '*' | Marker for significant values |
positive_color |
string | '#2166ac' | Color for r=+1 (blue) |
negative_color |
string | '#b2182b' | Color for r=-1 (red) |
neutral_color |
string | '#f7f7f7' | Color for r=0 (white) |
lower_triangle |
boolean | false | Show only lower triangle |
upper_triangle |
boolean | false | Show only upper triangle |
show_diagonal |
boolean | true | Show diagonal elements |
method |
string | 'pearson' | Correlation method: 'pearson' or 'spearman' |
Sankey flow diagram showing flows between nodes with proportional-width connections.
import { geom_sankey } from '@ggterm/core'
const flows = [
{ source: 'Coal', target: 'Electricity', value: 25 },
{ source: 'Gas', target: 'Electricity', value: 20 },
{ source: 'Coal', target: 'Heat', value: 10 },
]
gg(flows)
.aes({ x: 'source', y: 'target', fill: 'value' })
.geom(geom_sankey())
.labs({ title: 'Energy Flow' })| Option | Type | Default | Description |
|---|---|---|---|
node_width |
number | 3 | Node bar width in characters |
node_padding |
number | 2 | Padding between nodes |
node_char |
string | '█' | Character for node bars |
flow_char |
string | '─' | Character for flow lines |
show_labels |
boolean | true | Show node labels |
show_values |
boolean | false | Show flow values |
align |
string | 'justify' | Node alignment: 'left', 'right', 'center', 'justify' |
color_by |
string | 'auto' | Color scheme: 'auto', 'source', 'target' |
min_flow_width |
number | 1 | Minimum flow line width |
Treemap visualization displaying hierarchical data as nested rectangles where area represents value.
import { geom_treemap } from '@ggterm/core'
const data = [
{ name: 'JavaScript', value: 300 },
{ name: 'Python', value: 250 },
{ name: 'TypeScript', value: 200 },
]
gg(data)
.aes({ x: 'name', fill: 'value' })
.geom(geom_treemap())
.labs({ title: 'Language Usage' })| Option | Type | Default | Description |
|---|---|---|---|
algorithm |
string | 'squarify' | Tiling: 'squarify', 'binary', 'slice', 'dice' |
show_labels |
boolean | true | Show labels in rectangles |
show_values |
boolean | false | Show values in rectangles |
border |
boolean | true | Draw borders around rectangles |
padding |
number | 0 | Padding between nested rectangles |
min_label_size |
number | 4 | Minimum size to show label |
color_by |
string | 'value' | Color by: 'value', 'depth', 'parent' |
fill_char |
string | '█' | Fill character |
max_depth |
number | unlimited | Maximum hierarchy depth to show |
aspect_ratio |
number | 1.618 | Target aspect ratio (golden ratio) |
Volcano plot for differential expression analysis. Visualizes statistical significance vs magnitude of change, commonly used in genomics.
import { geom_volcano } from '@ggterm/core'
const data = [
{ gene: 'TP53', log2FC: 3.5, pvalue: 1e-50 },
{ gene: 'BRCA1', log2FC: -2.8, pvalue: 1e-40 },
{ gene: 'MYC', log2FC: 2.1, pvalue: 1e-30 },
{ gene: 'GAPDH', log2FC: 0.1, pvalue: 0.5 },
]
gg(data)
.aes({ x: 'log2FC', y: 'pvalue', label: 'gene' })
.geom(geom_volcano({
fc_threshold: 1,
p_threshold: 0.05,
n_labels: 5,
}))
.labs({ title: 'Differential Expression' })| Option | Type | Default | Description |
|---|---|---|---|
fc_threshold |
number | 1 | Log2 fold change threshold (1 = 2-fold) |
p_threshold |
number | 0.05 | P-value threshold for significance |
y_is_neglog10 |
boolean | false | If true, y values are already -log10 transformed |
up_color |
string | '#e41a1c' | Color for up-regulated points (red) |
down_color |
string | '#377eb8' | Color for down-regulated points (blue) |
ns_color |
string | '#999999' | Color for non-significant points (gray) |
show_thresholds |
boolean | true | Show threshold reference lines |
threshold_linetype |
string | 'dashed' | Line type for thresholds |
n_labels |
number | 0 | Number of top significant points to label |
size |
number | 1 | Point size |
alpha |
number | 0.6 | Point opacity |
point_char |
string | '●' | Character for points |
Points are automatically classified as:
- Up-regulated: log2FC > fc_threshold AND p < p_threshold
- Down-regulated: log2FC < -fc_threshold AND p < p_threshold
- Not significant: everything else
MA plot for differential expression analysis. Plots log2 fold change (M) against average expression (A), commonly used alongside volcano plots in genomics.
import { geom_ma } from '@ggterm/core'
const data = [
{ gene: 'TP53', baseMean: 5000, log2FC: 3.5, padj: 1e-50 },
{ gene: 'BRCA1', baseMean: 3000, log2FC: -2.8, padj: 1e-40 },
{ gene: 'MYC', baseMean: 8000, log2FC: 2.1, padj: 1e-30 },
{ gene: 'GAPDH', baseMean: 50000, log2FC: 0.1, padj: 0.9 },
]
gg(data)
.aes({ x: 'baseMean', y: 'log2FC', label: 'gene' })
.geom(geom_ma({
fc_threshold: 1,
p_col: 'padj',
p_threshold: 0.05,
n_labels: 5,
}))
.labs({ title: 'MA Plot - Differential Expression' })| Option | Type | Default | Description |
|---|---|---|---|
fc_threshold |
number | 1 | Log2 fold change threshold (1 = 2-fold) |
p_threshold |
number | 0.05 | P-value threshold for significance |
p_col |
string | undefined | Column name containing p-values (optional) |
x_is_log2 |
boolean | false | If true, x values are already log2 transformed |
up_color |
string | '#e41a1c' | Color for up-regulated points (red) |
down_color |
string | '#377eb8' | Color for down-regulated points (blue) |
ns_color |
string | '#999999' | Color for non-significant points (gray) |
show_baseline |
boolean | true | Show M=0 baseline reference line |
show_thresholds |
boolean | true | Show ±fc_threshold reference lines |
linetype |
string | 'dashed' | Line type for reference lines |
n_labels |
number | 0 | Number of top significant points to label |
size |
number | 1 | Point size |
alpha |
number | 0.6 | Point opacity |
point_char |
string | '●' | Character for points |
Points are classified as:
- Up-regulated: log2FC > fc_threshold (AND p < p_threshold if p_col specified)
- Down-regulated: log2FC < -fc_threshold (AND p < p_threshold if p_col specified)
- Not significant: everything else
Manhattan plot for GWAS (Genome-Wide Association Study) visualization. Shows genomic position vs -log10(p-value) with significance thresholds.
import { geom_manhattan } from '@ggterm/core'
const data = [
{ snp: 'rs123', chr: '1', pos: 1000000, pvalue: 1e-9 },
{ snp: 'rs456', chr: '2', pos: 2000000, pvalue: 1e-6 },
{ snp: 'rs789', chr: '6', pos: 3000000, pvalue: 5e-8 },
]
gg(data)
.aes({ x: 'pos', y: 'pvalue', color: 'chr', label: 'snp' })
.geom(geom_manhattan({
genome_wide_threshold: 5e-8,
suggestive_threshold: 1e-5,
n_labels: 5,
}))
.labs({ title: 'GWAS Results' })| Option | Type | Default | Description |
|---|---|---|---|
suggestive_threshold |
number | 1e-5 | Suggestive significance threshold |
genome_wide_threshold |
number | 5e-8 | Genome-wide significance threshold |
y_is_neglog10 |
boolean | false | If true, y values are already -log10 transformed |
chr_colors |
string[] | ['#1f78b4', '#a6cee3'] | Alternating chromosome colors |
highlight_color |
string | '#e41a1c' | Color for genome-wide significant points |
suggestive_color |
string | '#ff7f00' | Color for suggestive points |
show_thresholds |
boolean | true | Show significance threshold lines |
n_labels |
number | 0 | Number of top SNPs to label |
chr_gap |
number | 0.02 | Gap between chromosomes as fraction |
Heatmap for matrix visualization with optional hierarchical clustering. Commonly used for gene expression matrices.
import { geom_heatmap } from '@ggterm/core'
const data = [
{ gene: 'IL6', sample: 'Control', expression: 0.5 },
{ gene: 'IL6', sample: 'Treatment', expression: 3.2 },
{ gene: 'TNF', sample: 'Control', expression: 1.0 },
{ gene: 'TNF', sample: 'Treatment', expression: 2.5 },
]
gg(data)
.aes({ x: 'sample', y: 'gene', fill: 'expression' })
.geom(geom_heatmap({
cluster_rows: true,
cluster_cols: true,
scale: 'row',
}))
.labs({ title: 'Gene Expression' })| Option | Type | Default | Description |
|---|---|---|---|
low_color |
string | '#313695' | Color for low values (blue) |
mid_color |
string | '#ffffbf' | Color for mid values (yellow) |
high_color |
string | '#a50026' | Color for high values (red) |
na_color |
string | '#808080' | Color for missing values |
cluster_rows |
boolean | false | Cluster rows hierarchically |
cluster_cols |
boolean | false | Cluster columns hierarchically |
clustering_method |
string | 'complete' | 'complete', 'single', or 'average' |
scale |
string | 'none' | 'none', 'row', or 'column' |
show_row_labels |
boolean | true | Show row labels |
show_col_labels |
boolean | true | Show column labels |
PCA biplot showing both samples (scores) and variables (loadings) together.
import { geom_biplot } from '@ggterm/core'
const scores = [
{ sample: 'A', species: 'setosa', PC1: -2.5, PC2: 0.5 },
{ sample: 'B', species: 'versicolor', PC1: 0.5, PC2: -0.5 },
{ sample: 'C', species: 'virginica', PC1: 2.5, PC2: 0.2 },
]
const loadings = [
{ variable: 'Sepal.L', pc1: 0.52, pc2: -0.38 },
{ variable: 'Petal.L', pc1: 0.58, pc2: 0.02 },
]
gg(scores)
.aes({ x: 'PC1', y: 'PC2', color: 'species' })
.geom(geom_biplot({
loadings,
var_explained: [0.73, 0.23],
}))
.labs({ title: 'PCA Biplot', x: 'PC1 (73%)', y: 'PC2 (23%)' })| Option | Type | Default | Description |
|---|---|---|---|
pc1_col |
string | 'PC1' | Column name for PC1 scores |
pc2_col |
string | 'PC2' | Column name for PC2 scores |
loadings |
array | undefined | Array of {variable, pc1, pc2} for arrows |
var_explained |
[number, number] | undefined | Variance explained by PC1, PC2 |
show_scores |
boolean | true | Show sample points |
show_loadings |
boolean | true | Show loading arrows |
loading_color |
string | '#e41a1c' | Color for loading arrows |
loading_scale |
number | auto | Scale factor for arrows |
show_origin |
boolean | true | Show origin crosshairs |
show_loading_labels |
boolean | true | Show variable names |
Kaplan-Meier survival curves for time-to-event analysis.
import { geom_kaplan_meier } from '@ggterm/core'
const survivalData = [
{ time: 10, status: 1, treatment: 'Control' },
{ time: 20, status: 0, treatment: 'Control' }, // Censored
{ time: 15, status: 1, treatment: 'Treatment' },
]
gg(survivalData)
.aes({ x: 'time', y: 'status', color: 'treatment' })
.geom(geom_kaplan_meier({ show_censored: true }))
.labs({ title: 'Survival by Treatment', x: 'Time', y: 'Survival Probability' })| Option | Type | Default | Description |
|---|---|---|---|
show_ci |
boolean | false | Show confidence intervals |
conf_level |
number | 0.95 | Confidence level for CI |
show_censored |
boolean | true | Show censored observations |
censor_char |
string | '+' | Character for censored marks |
show_median |
boolean | false | Show median survival line |
step_type |
string | 'post' | Step style ('post' or 'pre') |
Forest plots for meta-analysis visualization.
import { geom_forest } from '@ggterm/core'
const metaData = [
{ study: 'Smith 2020', estimate: 1.25, ci_lower: 0.95, ci_upper: 1.65 },
{ study: 'Jones 2021', estimate: 1.45, ci_lower: 1.10, ci_upper: 1.90 },
]
gg(metaData)
.aes({ x: 'estimate', y: 'study', xmin: 'ci_lower', xmax: 'ci_upper' })
.geom(geom_forest({ null_line: 1 }))
.labs({ title: 'Meta-Analysis', x: 'Odds Ratio' })| Option | Type | Default | Description |
|---|---|---|---|
null_line |
number | 1 | Position of null effect line |
log_scale |
boolean | false | Use log scale for x-axis |
show_summary |
boolean | false | Show diamond for pooled estimate |
null_line_color |
string | '#888888' | Color for null line |
point_char |
string | '■' | Character for point estimates |
ROC curves for classifier evaluation.
import { geom_roc } from '@ggterm/core'
const rocData = [
{ fpr: 0.0, tpr: 0.0 },
{ fpr: 0.1, tpr: 0.5 },
{ fpr: 0.3, tpr: 0.8 },
{ fpr: 1.0, tpr: 1.0 },
]
gg(rocData)
.aes({ x: 'fpr', y: 'tpr' })
.geom(geom_roc({ show_auc: true }))
.labs({ title: 'ROC Curve', x: 'FPR', y: 'TPR' })| Option | Type | Default | Description |
|---|---|---|---|
show_diagonal |
boolean | true | Show diagonal reference line |
show_auc |
boolean | true | Show AUC value |
show_optimal |
boolean | false | Show optimal threshold point |
diagonal_color |
string | '#888888' | Color for diagonal line |
fill_auc |
boolean | false | Fill area under curve |
Bland-Altman plots for method comparison.
import { geom_bland_altman } from '@ggterm/core'
const comparisonData = [
{ method1: 10.2, method2: 10.5 },
{ method1: 15.3, method2: 15.1 },
{ method1: 20.8, method2: 21.2 },
]
gg(comparisonData)
.aes({ x: 'method1', y: 'method2' })
.geom(geom_bland_altman())
.labs({ title: 'Method Comparison' })| Option | Type | Default | Description |
|---|---|---|---|
show_limits |
boolean | true | Show limits of agreement |
show_bias |
boolean | true | Show mean bias line |
limit_multiplier |
number | 1.96 | Multiplier for limits (1.96 = 95%) |
bias_color |
string | '#0000ff' | Color for bias line |
limit_color |
string | '#ff0000' | Color for limit lines |
percent_diff |
boolean | false | Use percentage difference |
precomputed |
boolean | false | Data has mean/diff columns |
Add arbitrary text, shapes, and lines to plots.
import { annotate } from '@ggterm/core'
gg(data)
.aes({ x: 'x', y: 'y' })
.geom(geom_point())
.geom(annotate('text', { x: 5, y: 10, label: 'Important point' }))
.geom(annotate('rect', { xmin: 2, xmax: 4, ymin: 5, ymax: 15, alpha: 0.2 }))
.geom(annotate('hline', { y: 50, linetype: 'dashed', color: 'red' }))| Type | Required Options | Description |
|---|---|---|
text |
x, y, label | Text label |
label |
x, y, label | Text with background |
rect |
xmin, xmax, ymin, ymax | Rectangle |
segment |
x, y, xend, yend | Line segment |
hline |
y | Horizontal line |
vline |
x | Vertical line |
abline |
slope, intercept | Arbitrary line |
point |
x, y | Single point |
import {
annotate_text,
annotate_label,
annotate_rect,
annotate_segment,
annotate_hline,
annotate_vline
} from '@ggterm/core'
// Shorthand versions
annotate_text(5, 10, 'Label')
annotate_rect(0, 5, 0, 10, { alpha: 0.2 })
annotate_hline(50, { linetype: 'dashed' })import {
scale_size_continuous,
scale_size_area,
scale_size_radius,
scale_size_binned
} from '@ggterm/core'
// Continuous size mapping
gg(data)
.aes({ x: 'x', y: 'y', size: 'value' })
.scale(scale_size_continuous({ range: [1, 10] }))
// Area-proportional sizing (for bubble charts)
.scale(scale_size_area({ max_size: 10 }))
// Binned sizes
.scale(scale_size_binned({ n_breaks: 5 }))import {
scale_shape_discrete,
scale_shape_manual,
DEFAULT_SHAPES,
SHAPE_CHARS
} from '@ggterm/core'
// Automatic shape assignment
gg(data)
.aes({ x: 'x', y: 'y', shape: 'category' })
.scale(scale_shape_discrete())
// Manual shape mapping
.scale(scale_shape_manual({
values: {
'A': 'circle',
'B': 'square',
'C': 'triangle'
}
}))circle, square, triangle, diamond, cross, plus, star,
open_circle, open_square, open_triangle, open_diamond, dot
import {
scale_alpha_continuous,
scale_alpha_discrete,
scale_alpha_manual
} from '@ggterm/core'
// Continuous alpha
gg(data)
.aes({ x: 'x', y: 'y', alpha: 'importance' })
.scale(scale_alpha_continuous({ range: [0.2, 1.0] }))
// Discrete alpha
.scale(scale_alpha_discrete({ values: [0.3, 0.6, 1.0] }))import {
scale_x_datetime,
scale_y_datetime,
scale_x_date,
scale_x_time,
scale_x_duration
} from '@ggterm/core'
// Full datetime
gg(data)
.aes({ x: 'timestamp', y: 'value' })
.scale(scale_x_datetime())
// Date only
.scale(scale_x_date())
// Time of day (0-24 hours)
.scale(scale_x_time({ domain: [9, 17] })) // 9 AM to 5 PM
// Duration
.scale(scale_x_duration({ unit: 'minutes' }))High-performance real-time plotting.
import { createStreamingPlot, createTimeSeriesPlot } from '@ggterm/core'
// Simple streaming plot
const plot = createStreamingPlot({
maxPoints: 100,
windowMs: 60000 // 1 minute window
})
// Add data
plot.push({ time: Date.now(), value: 42 })
// Render
console.log(plot.render({ width: 80, height: 20 }))Sliding window for time-series data.
import { createDataWindow } from '@ggterm/core'
const window = createDataWindow({
maxSize: 1000,
timeField: 'timestamp',
windowMs: 60000
})
window.push({ timestamp: Date.now(), value: 42 })
// Get windowed data
const data = window.getData()
const stats = window.getStats() // { min, max, mean, count }Compute rolling statistics.
import { createRollingAggregator } from '@ggterm/core'
const agg = createRollingAggregator({
windowSize: 10,
aggregation: 'mean' // 'mean', 'sum', 'min', 'max', 'count'
})
agg.push(42)
const rollingMean = agg.getValue()Handle large datasets efficiently.
import {
autoSample,
systematicSample,
randomSample,
lttbSample // Largest Triangle Three Buckets
} from '@ggterm/core'
// Auto-select best method
const sampled = autoSample(largeData, { targetSize: 1000 })
// LTTB for time series (preserves visual shape)
const sampled = lttbSample(data, 500, 'time', 'value')Automatic detail reduction for large datasets.
import { createLOD } from '@ggterm/core'
const lod = createLOD(data, {
levels: [
{ threshold: 1000, method: 'systematic', targetSize: 500 },
{ threshold: 10000, method: 'lttb', targetSize: 1000 }
]
})
// Get appropriate detail level
const displayData = lod.getData(currentZoomLevel)Efficient re-rendering.
import { createCanvasDiff } from '@ggterm/core'
const diff = createCanvasDiff()
// Only render changed cells
const changes = diff.compare(previousCanvas, currentCanvas)
const output = diff.renderChanges(changes)import {
detectCapabilities,
getCapabilities,
getRecommendedRenderer
} from '@ggterm/core'
const caps = detectCapabilities()
// {
// colorCapability: 'truecolor',
// graphicsProtocol: 'sixel',
// unicodeSupport: true,
// terminalSize: { width: 120, height: 40 }
// }
const renderer = getRecommendedRenderer()
// 'sixel', 'braille', or 'block'import {
rgbToAnsi256,
rgbToAnsi16,
findClosestPaletteColor,
quantizeColor
} from '@ggterm/core'
// Convert RGB to nearest 256-color
const ansi = rgbToAnsi256(255, 128, 0)
// Quantize for limited palettes
const quantized = quantizeColor({ r: 255, g: 128, b: 0, a: 1 }, '256')ggterm includes AI-assisted skills for Claude Code that enable natural language interaction with plots.
| Skill | Trigger | Description |
|---|---|---|
data-load |
"load data", "read csv" | Load CSV, JSON, JSONL files |
ggterm-plot |
"plot", "chart", "visualize" | Create terminal visualizations |
ggterm-history |
"show history", "find plot" | Search and retrieve historical plots |
ggterm-markdown |
"create report", "export markdown" | Generate reports with embedded plots |
ggterm-publish |
"export", "save as png" | Export to PNG/SVG/PDF/HTML |
ggterm-customize |
"change colors", "make title bigger" | Natural language plot customization |
ggterm-style |
"style like Wilke", "Tufte style" | Apply publication-quality style presets |
Apply expert-curated style presets for publication-ready output.
# In Claude Code, after creating and exporting a plot:
/ggterm-style wilke
/ggterm-style tufte
/ggterm-style economistBased on Fundamentals of Data Visualization:
- Font: Helvetica Neue (clean sans-serif)
- Gridlines: Y-axis only, subtle gray (#ebebeb)
- Axis titles: Normal weight, dark gray
- Colors: Colorblind-safe categorical palette
- Best for: Academic papers, general publication
Maximum data-ink ratio principles:
- Font: Georgia (elegant serif)
- No gridlines, no axis lines, no tick marks
- Grayscale color palette
- Shape encoding for redundancy
- Best for: Minimalist presentations, data-focused reports
Distinctive editorial magazine style:
- Background: Light blue-gray (#d5e4eb)
- Gridlines: White horizontal lines
- X-axis: Bold black baseline
- Colors: Bold Economist palette (blue, cyan, orange)
- Best for: Editorial content, magazine publication
Nature publication specifications:
- Dimensions: 180x150px (compact)
- Fonts: 8-10pt Arial
- No grid
- Best for: Nature/Science journal submission
American Psychological Association style:
- Font: Times New Roman (serif)
- Axis titles: Italic
- Colors: Grayscale
- Best for: Psychology papers, academic publication
Generic distraction-free style:
- Font: System UI
- No grid, no borders, no ticks
- Best for: Web, presentations
# 1. Create a plot
npx ggterm-plot data.csv x y color "Title" point
# 2. Export to save Vega-Lite spec
npx ggterm-plot export output.html
# 3. Apply style (in Claude Code)
/ggterm-style wilke
# 4. View styled result
open /tmp/styled-plot.htmlStyles modify the Vega-Lite config object:
{
config: {
font: "Helvetica Neue, Arial, sans-serif",
background: "white",
view: { stroke: null },
title: {
fontSize: 14,
fontWeight: "normal",
anchor: "start"
},
axis: {
domain: true,
domainColor: "#333333",
grid: false,
labelFontSize: 11,
titleFontWeight: "normal"
},
axisY: {
grid: true,
gridColor: "#ebebeb"
},
legend: {
labelFontSize: 11,
orient: "right"
},
range: {
category: ["#4C78A8", "#F58518", "#E45756", ...]
}
}
}import type {
// Core types
Plot,
PlotSpec,
Geom,
Stat,
Scale,
Coord,
Facet,
Theme,
AestheticMapping,
DataSource,
DataRecord,
RenderOptions,
// Canvas types
Canvas,
CanvasCell,
RGBA,
// Scale types
Domain,
Range,
SizeScaleOptions,
ShapeScaleOptions,
AlphaScaleOptions,
DateTimeScaleOptions,
// Geom types
ViolinOptions,
TileOptions,
ContourOptions,
ErrorbarOptions,
RectOptions,
// Streaming types
StreamingPlotOptions,
WindowOptions,
BufferOptions,
SamplingOptions,
LODLevel,
// Terminal types
ColorCapability,
GraphicsProtocol,
TerminalCapabilities,
RendererType
} from '@ggterm/core'