Beautiful and
interactive plots,
right inside
your terminal

plotui draws interactive, Plotly-style 2D & 3D charts inside the terminal — rasterized by a Rust engine, delivered over the Kitty graphics protocol, embeddable in TUI frameworks such as Textual, Ratatui, or Bubble Tea. Select, rotate, pan, zoom, hover.

API

A plotting API you already know.

The same calls in Python, Rust, and Go, so examples translate one-to-one.

  • 2D and 3D in one object — scatter, line, and bar; 3D scatter, surfaces, and graphs. The first 3D trace turns on the orbit camera.
  • Interactive and pickable — orbit, zoom, and pan; hover highlights, and a click tells you what was picked.
  • Drops in as a widget — a first-class widget for Textual, Ratatui, and Bubble Tea, or drive the engine yourself from any event loop.
from plotui import Plot
from plotui.textual import PlotWidget

plot = Plot()

# the 3D scene in the demo up top — legend and colors, automatic
plot.add_scatter3d(x_a, y_a, z_a, name="Cluster A")
plot.add_scatter3d(x_b, y_b, z_b, name="Cluster B")
plot.add_scatter3d(x_c, y_c, z_c, name="Cluster C")

# in a Textual app: drop it in as a widget, done
yield PlotWidget(plot)

# or drive it yourself from any event loop
plot.rotate(d_yaw, d_pitch)
escape = plot.render_kitty(cols, rows, cell_w, cell_h)

compatibility

Your framework, your terminal — supported.

A first-class widget for your TUI framework, real pixels in your terminal. If your pair is below, plotui just works — and feels the same everywhere: drag rotates, shift-drag pans, scroll zooms, clicks pick.

TUI frameworks

Textual

python

Drop PlotWidget into any Textual app — rotate, zoom, hover and click picking, live streaming updates. One import and you're plotting.

from plotui.textual import PlotWidget

Ratatui

rust

A native StatefulWidget — draw it like any other widget, hand it your events, and get flicker-free interactive plots in your Rust TUI.

use plotui_ratatui::PlotWidget;

Bubble Tea

go

The teaplot component for Bubble Tea v2 — Elm-style Update/View, with pick and hover messages ready for your update loop.

github.com/sebaheg/plotui/go/teaplot

Terminals

Kittyplaceholder
Ghosttyplaceholder
iTerm2 ≥ 3.5direct
WezTermdirect
Konsoledirect
Warpdirect · partial
Riodirect · partial
VS Codedirect · partial
placeholder
the richest mode — plots are woven into the character grid, so they scroll, split, and repaint exactly like text
direct
pixels drawn straight at the plot's position, just as sharp — plotui manages repaints itself
~ partial
these terminals' pixel-graphics support is new and still maturing — plots work, with occasional rough edges

streaming

Supports streaming data

Every add_* call returns a stable trace handle. Appends through it cost only the new points — not the history — and axes autoscale on the next frame. numpy arrays cross into the Rust core in one bulk copy.

# handles come back from add_*
h = plot.add_line([], [], name="forecast")
o = plot.add_scatter([], [], name="observed")
g = plot.add_line([], [], name="load", axis="y2")

def feed():  # 20 times a second
    widget.extend(h, [t], [forecast(t)])
    widget.extend(o, [t], [measured(t)])
    widget.extend(g, [t], [load(t)])

widget.set_visible(o, False)  # legend toggle

installation

Several install options

The plotui CLI ships prebuilt for macOS and Linux — plot straight from your shell: seq 100 | plotui line. Python users get the CLI bundled with the library wheel.

# prebuilt binary, macOS & Linux:
❯ curl -fsSL https://plotui.xyz/install.sh | sh

# then run a built-in demo:
❯ plotui example scatter

feedback

Found a bug? Missing a feature?

plotui is young and moving fast — bug reports and feature requests genuinely shape what gets built next. If a plot renders wrong in your terminal, your setup isn't covered, or you're missing a chart type, say so on GitHub.