Every plot below is rasterized by plotui-core — the same Rust code that draws into your terminal — compiled to WebAssembly. Nothing here is a mock-up: the camera, the picking, the legends and the crosshair are the engine's own. Drag, zoom, hover.
from plotui import Plot
p = Plot()
p.add_scatter3d(xs_a, ys_a, zs_a, color=(236, 76, 134))
p.add_scatter3d(xs_b, ys_b, zs_b, color=(69, 200, 209))
p.add_scatter3d(xs_c, ys_c, zs_c, color=(240, 161, 60))
# drag events → p.rotate(dyaw, dpitch); hover → p.pick_px(...)
from plotui import Plot
p = Plot()
p.add_surface3d(xs, ys, zs, colormap="viridis")
# zs is a len(ys) × len(xs) grid of heights
p.add_surface3d(xs, ys, zs, colormap=None,
color=(103, 111, 118), wireframe=True)
from plotui import Plot
p = Plot()
p.add_graph3d(xs, ys, zs, edges=[(0, 1), (1, 2), ...],
color=(69, 200, 209))
hit = p.pick_element_px(w, h, mx, my, node_radius=8)
# hit → ("node", i) or ("edge", i); highlight via p.set_hovered(hit)
from plotui import ForceLayout, Plot
lay = ForceLayout(n, edges, seed=7)
h = p.add_graph3d(*lay.positions(), edges=edges)
# per tick: step the physics, move the nodes in place
if lay.step() >= 1e-3:
p.set_graph_positions(h, *lay.positions())
# hover: recolor the dependency closure, restore on leave
p.set_graph_colors(h, node_colors, edge_colors)
from plotui import Plot
p = Plot()
h = p.add_scatter3d([], [], [], color=(69, 200, 209))
# stream points in as they arrive — appended in place,
# no rebuilds, while the camera keeps moving
for xs, ys, zs in scanner:
p.extend(h, xs, ys, zs)
// meshes are Rust and JS today; the Python binding is still to come
use plotui_core::{marching_cubes, Colormap, Plot};
// sample any scalar field on a grid, then polygonise it
let (verts, tris) = marching_cubes(&values, nx, ny, nz,
[-1.2, -1.2, -1.2], cell, 0.0);
let mut p = Plot::new();
p.add_mesh3d(verts, tris, color, Some(Colormap::Plasma), None);
// shared edge vertices → smooth Gouraud shading across cells
// plotui knows nothing about proteins — it sweeps polylines.
// the PDB parsing and the peptide plane live in the caller.
use plotui_core::{catmull_rom, ribbon, tube, Plot};
let path = catmull_rom(&alpha_carbons, 12); // 3.8 Å apart is too coarse
// a flat band, turned by the peptide plane, tapering to an arrowhead
let (verts, tris) = ribbon(&path, &normals, &widths, 0.45);
let (verts, tris) = tube(&path, &[0.32], 8); // loops, as round tube
p.add_mesh3d(verts, tris, color, None, name);
from plotui import Plot
p = Plot()
h = p.add_line3d([], [], [], color="#c03c80", width=1.4)
# integrate and append — the curve grows in place,
# no rebuilds, while the camera keeps orbiting
for xs, ys, zs in rk4_steps(aizawa):
p.extend(h, xs, ys, zs)
# a NaN vertex breaks the line, so one trace can hold
# many runs — that's how the speed bands stay separate
from plotui import Plot
p = Plot()
p.add_line3d(xs, ys, zs, color=(236, 76, 134),
width=1.5, name="lorenz")
# while dragging, plotui renders at reduced resolution —
# the same policy the terminal widget uses
from plotui import Plot
p = Plot()
p.add_line(xs, damped, name="damped")
p.add_line(xs, carrier, name="carrier")
p.add_scatter(sample_xs, sample_ys, name="samples")
p.set_hover2d(mouse_x) # engine draws the crosshair + readout
from plotui import Plot
p = Plot()
p.add_bar(months, totals, color=(69, 200, 209), name="total")
p.add_line(months, trend, color=(240, 161, 60), name="trend")
from plotui import Plot
p = Plot()
p.add_step(ts, workers, where_="post", name="workers")
p.add_line(ts, queue, name="queue depth")
# a straight segment would draw a transition that never happened
from plotui import Plot
p = Plot()
p.add_histogram(samples, name="latency (ms)")
# bins chosen by Freedman-Diaconis; extend_values streams more in
from plotui import Plot
p = Plot()
p.add_heatmap(hours, days, rate, colormap="viridis", label="requests/s")
p.set_categories("y", ["Mon", "Tue", ...])
from plotui import Plot
p = Plot()
p.set_barmode("stack") # or "group"
p.add_bar(months, solar, name="solar")
p.add_bar(months, wind, name="wind")
p.add_bar(months, hydro, name="hydro")
from plotui import Plot
p = Plot()
p.add_band(hours, lo, hi, name="95% interval") # before the line
p.add_line(hours, mid, name="forecast")
h = p.add_scatter(obs_x, obs_y, name="observed")
p.set_error_bars(h, y_plus=err)
from plotui import Plot
p = Plot()
p.add_box([ci_a, ci_b, ci_c, ci_d], name="build time (s)")
p.set_categories("x", ["ci-a", "ci-b", "ci-c", "ci-d"])
# whiskers stop at real data; stragglers stay their own points