Textplot
ASCII and Unicode plots inline in DuckDB query results β bars, sparklines, and density plots that fit in a single VARCHAR column.
On this page
Technical Overview
See the shape of the data without leaving the CLI
Textplot renders ASCII and Unicode charts as VARCHAR values right inside a SELECT. The point: when you're poking at data in the DuckDB CLI, you can see the shape of a distribution, the trend of a series, or the level of a metric without copying numbers into a notebook or a BI tool. Quick eyeballing β not a replacement for real visualization when you actually need axes, hover, or pixels.
What it is
Inline visuals for terminal-first SQL workflows. When you're at a DuckDB prompt staring at a column of numbers, the usual way to understand its shape is to leave SQL β copy the values into a notebook, draw a chart, come back. Textplot removes that detour: a distribution, a per-entity trend, or a status level becomes a chart you read directly in the result table. It pairs naturally with fuzzycomplete as the two extensions worth installing for an "I live in the REPL" workflow β one makes typing queries painless, the other makes reading their output painless.
How it works
Every function returns a single VARCHAR. That is the entire architecture, and it is the load-bearing design choice: there is no client-side renderer, no asset pipeline, no extra dependency in the query plan. A chart is just another string column, so the glyphs travel anywhere a text column can go.
-
β’
Charts are plain text: Because the output is a
VARCHAR, a generated chart composes with.mode line,.mode markdown, log redirection,teeto a file, a status email, or a README paste β exactly like any other string column, with no rendering step in between. - β’ Fixed-width alignment: Width is a parameter, so charts line up cleanly column-over-column across rows in a result table β which is what makes per-row sparklines and bars readable as a fleet view.
What it isn't
Textplot is a CLI ergonomics tool. It earns its place when the alternative is squinting at numbers β not when you actually need a real chart.
- β’ Not a visualization library: Character-grid resolution caps detail. For interactive dashboards, publication figures, or anything where pixels matter, use a real charting tool β Matplotlib, Vega-Lite, Observable Plot, Apache Superset, etc.
-
β’
Not a statistical summary: A density plot shows the shape of a distribution; it doesn't compute moments, fit kernels, or give you confidence intervals. Pair it with
quantile_cont,stddev_pop, ordatasketcheswhen you need numbers. - β’ Unicode rendering varies: Emoji and block characters depend on the terminal font, so the same chart can look different across environments. For maximum portability, fall back to the ASCII styles or supply your own characters.
Deep Dive
Technical Details
What you can do with one query
The shortest path from βa column of numbersβ to βI can see whatβs going onβ β without leaving the DuckDB CLI:
SELECT tp_density(array_agg(value)) AS distributionFROM measurements;tp_density renders the array as a one-line Unicode density plot β enough to tell whether the data is roughly normal, bimodal, or has a long tail. No notebook, no matplotlib, no BI tool round-trip. The same shape works for sparklines and threshold-colored bars in any result table:
SELECT host, tp_sparkline(cpu_history, theme := 'utf8_blocks', width := 24) AS cpu, tp_bar(disk_pct, min := 0, max := 100, width := 12, thresholds := [(90, 'red'), (75, 'yellow'), (0, 'green')]) AS diskFROM hosts_statusORDER BY disk_pct DESC;One sparkline per row, one threshold-colored bar per row, all inline in the result table.
Textplot is a CLI ergonomics tool. Character-grid resolution caps the detail you can see β these plots are for quick visual checks during interactive SQL, not a replacement for Matplotlib, Vega-Lite, or a proper BI dashboard. When you need axes, hover, drilldown, or pixels, use a real charting tool. When you just want to know the shape of a column without leaving the terminal, use this.
How it fits the CLI workflow
Every function returns a single VARCHAR. Thatβs the whole API: any chart you produce is just another column in the result set. It composes with .mode line, .mode markdown, log redirection, tee to a file, or paste into a status email β same as any other string column.
The companion piece for the CLI is fuzzycomplete, which fixes tab-completion for table and column names. Together theyβre the two extensions worth installing for βI live in the DuckDB REPLβ workflows: fuzzycomplete makes typing queries painless, textplot makes reading their output painless.
Function map
| Function | Shape | Use for |
|---|---|---|
tp_bar |
Single bar | Progress, percentages, SLA indicators |
tp_sparkline |
One-line trend | Time series, KPIs in tables |
tp_density |
Distribution plot | Spotting outliers, shape of a column |
tp_qr |
QR code | Scannable links in terminal/docs |
Styling vocabulary
The named-parameter surface is shared in spirit across functions:
- Shapes / styles / themes β
shape := 'circle' | 'square' | 'heart'ontp_bar; named character-set styles ontp_density(shaded,ascii,dots,height,circles,rainbow_circle, β¦); per-mode themes ontp_sparkline(utf8_blocks,arrows,faces,slopes, β¦). - Colors β
on_color/off_colorontp_barandtp_density.thresholds := [(threshold, color), ...]gives value-conditional coloring; each block uses the highest threshold it satisfies. - Custom characters β every function with
on/offaccepts arbitrary strings (single chars, emoji, multi-char sequences). Theyβre SQL reserved words β quote them as"on"and"off". - Width β fixed character width keeps charts aligned in tables.
Portability: ASCII vs Unicode
Defaults use Unicode block characters and emoji, which look great in modern terminals but vary by font. For maximum portability:
-- ASCII-only density plotSELECT tp_density([1, 5, 3, 8, 2], style := 'ascii');-- ASCII sparklineSELECT tp_sparkline([1, 3, 2, 5, 4, 6], theme := 'ascii_basic');-- ASCII bar with custom charactersSELECT tp_bar(0.7, "on" := '#', "off" := '-', width := 15);-- ASCII QR codeSELECT tp_qr('https://query.farm', "on" := '##', "off" := ' ');For QR codes specifically, .mode ascii in the CLI gives the cleanest rendering β the default table borders interact poorly with multi-line block output.
When to reach for it (and when not to)
Reach for Textplot when:
- Youβre in the DuckDB CLI doing ad-hoc analysis and want to see a distribution or trend without copying values out.
- You need per-row visual cells in a result table β sparklines per host, bars per SLA, etc.
- The output goes to a terminal, log file, status email, or README.
Reach for a real charting tool when:
- Youβre building an interactive web dashboard with hover/zoom/drilldown.
- You need precise pixel control for publication-quality figures.
- The audience expects polished graphics and the data deserves them.
The two are complementary, not competing β Textplot is the βIβm at a SQL prompt right nowβ tool.
Install
INSTALL textplot FROM community;
LOAD textplot;
Quick Start
See the shape of a column without leaving the CLI
SELECT tp_density(array_agg(value)) AS distribution
FROM measurements;
One sparkline per row in a status table
SELECT host, tp_sparkline(cpu_history, width := 24) AS cpu
FROM hosts_status;
Inline progress bar
SELECT tp_bar(0.75) AS progress;
QR code (ASCII / Unicode)
SELECT tp_qr('https://query.farm');
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
Charts
Render numeric values and arrays as Unicode/ASCII charts directly in your query results β bars, sparklines, and density plots that fit in a single VARCHAR cell. |
||
| tp_bar() | Render a horizontal bar showing a numeric value as a fraction of a min/max range. | |
| tp_density() | Render an array of numeric values as a density plot β a compact distribution visualization where the character intensity at each x-position reflects how many input values fall in that bin. | |
| tp_sparkline() | Render an array of numeric values as a sparkline β a compact one-line trend chart. | |
|
QR Codes
Encode strings as QR codes rendered as Unicode-block art. Useful for dashboards, terminal output, and embedding scannable links in docs. |
||
| tp_qr() | Encode a string as a QR code rendered with Unicode block characters. | |
No extension contents match that search.
API Reference
Function Documentation
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Recipes for inline ASCII / Unicode plots in the DuckDB CLI β distributions, trends, bars, and QR codes that live as VARCHAR columns in your SELECT output.
See the shape of a column
The headline use β point an aggregate at tp_density and youβve got a one-line distribution plot in CLI output:
SELECT tp_density(array_agg(value)) AS distributionFROM measurements;For a wider plot or a different character set:
SELECT tp_density(array_agg(value), width := 40, style := 'height') AS distributionFROM measurements;Available styles: shaded (default), ascii, dots, height, circles, safety, rainbow_circle, rainbow_square, moon, sparse, white. See tp_density for the full parameter list.
Sparkline per row in a status table
The pattern that makes tp_sparkline most useful β pre-aggregate a series per entity, render a sparkline cell:
SELECT host, tp_sparkline(cpu_history, theme := 'utf8_blocks', width := 24) AS cpu_24hFROM hosts_statusORDER BY host;Three modes change what the sparkline encodes:
-- Absolute mode (default) β height β valueSELECT tp_sparkline([45.2, 47.1, 46.8, 49.3, 52.1, 48.7]);-- Delta mode β direction of change between consecutive pointsSELECT tp_sparkline([100, 105, 102, 108, 95], mode := 'delta');-- Trend mode β direction with magnitudeSELECT tp_sparkline([100, 110, 105, 125, 90], mode := 'trend', theme := 'intensity');Threshold-colored health bars
tp_bar with a thresholds list is the natural shape for SLA, disk-utilization, or risk indicators. Each block adopts the highest threshold β€ value:
SELECT host, disk_pct, tp_bar(disk_pct, min := 0, max := 100, width := 14, thresholds := [ {'threshold': 90, 'color': 'red'}, {'threshold': 75, 'color': 'yellow'}, {'threshold': 0, 'color': 'green'} ]) AS diskFROM hosts_statusORDER BY disk_pct DESC;Sort by the underlying number; let the bar carry the visual.
ASCII output for portable terminals
When the destination doesnβt render emoji or wide Unicode reliably (older terminals, log aggregators, plain-text email), pin every function to its ASCII vocabulary:
-- DensitySELECT tp_density([1, 5, 3, 8, 2], style := 'ascii');-- SparklineSELECT tp_sparkline([1, 3, 2, 5, 4, 6], theme := 'ascii_basic');-- Bar with custom characters ("on" / "off" are reserved β quote them)SELECT tp_bar(0.7, "on" := '#', "off" := '-', width := 15);-- QR code in ASCII blocksSELECT tp_qr('https://query.farm', "on" := '##', "off" := ' ');Per-row charts on a generated series
Useful when youβre prototyping or want to demo the styling vocabulary without a real dataset:
SELECT round(n, 4) AS value, tp_bar(n, width := 14, shape := 'circle', off_color := 'black', filled := true, thresholds := [ (0.8, 'red'), (0.7, 'orange'), (0.6, 'yellow'), (0.0, 'green') ]) AS barFROM (SELECT random() AS n FROM generate_series(1, 10));QR codes for terminal output
-- Default monochromeSELECT tp_qr('Hello, World!');-- High error correction (more redundancy, slightly larger code)SELECT tp_qr('https://query.farm', ecc := 'high');-- Custom emoji stylingSELECT tp_qr('https://query.farm', ecc := 'high', "on" := 'π‘', "off" := 'β«');In the DuckDB CLI, .mode ascii gives the cleanest rendering of QR output β the default table borders interact poorly with the multi-line block grid.
Pair with fuzzycomplete for a CLI-first workflow
Textplot makes CLI output readable; fuzzycomplete makes CLI input tolerable (VS-Code-style fuzzy tab-completion across schemas and databases). Install both if you live in the DuckDB REPL:
INSTALL fuzzycomplete FROM community;LOAD fuzzycomplete;
INSTALL textplot FROM community;LOAD textplot;Tips
- Pick widths to match your output β width 20β30 for dashboards, 10β15 for compact reports, β₯40 for distribution shapes you want to actually read.
- Combine with raw numbers, donβt replace them β the visual is for shape; the number is for precision. Show both.
- Sparklines shine in tables β one cell per row is where they earn their keep.
- ASCII for portability, emoji for impact β emoji needs a Unicode-capable terminal and font.
- For real visualization, use a real tool β Textplot is for eyeballing in the CLI, not for the dashboard you ship to stakeholders.
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Intel Apple Silicon
- Windows x86_64
- WASM eh mvp threads
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 3.28 MB |
| Linux | aarch64 | 2.90 MB |
| macOS | Intel | 2.10 MB |
| macOS | Apple Silicon | 1.81 MB |
| Windows | x86_64 | 7.42 MB |
| WASM | eh | 68.4 KB |
| WASM | mvp | 62.0 KB |
| WASM | threads | 68.5 KB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported