plot
Deterministic SVG/HTML chart rendering.
plot builds deterministic chart specifications in pure Dune and renders them to
SVG or HTML — no external plotting library. It covers the common chart types (line, area, step, scatter, bar, grouped bars, histogram, and pie), multi-series
overlays, grids, subplot figures, file output via fs.write_text, and a
platform-native display window through show() where the VM supports it.
Chart types
Every builder returns a Chart you refine with chained methods (.title(...), .x_label(...), .grid(...), .legend(true), .size(w, h)) and then render with plot.svg(chart).
Line chart
The default for a trend over time. plot.line(xs, ys) (or plot.line(ys) for index-based x). Chain .grid(...) / .minor_grid(...) for a background mesh.
import plot;
months = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
users = [120.0, 135.0, 148.0, 172.0, 168.0, 205.0, 233.0, 258.0];
chart = plot.line(months, users)
.title("Monthly active users").x_label("month").y_label("users")
.grid(7).minor_grid(2);
Multiple series
Overlay series by chaining add_line / add_scatter / add_bar on one chart. .label(...) names the most recently added series and legend(true) draws the key.
import plot;
chart = plot.line([30.0, 42.0, 39.0, 55.0, 61.0, 74.0]).label("revenue")
.add_line([22.0, 28.0, 31.0, 36.0, 41.0, 48.0]).label("cost")
.title("Revenue vs cost").x_label("quarter").y_label("$k")
.legend(true).grid(6).minor_grid(2);
Area chart
plot.area(...) is a line filled down to the baseline — good for showing a cumulative quantity or emphasising volume under a curve.
import plot;
chart = plot.area([4.0, 9.0, 7.0, 14.0, 20.0, 18.0, 27.0])
.title("Daily downloads").x_label("day").y_label("thousands")
.grid(6).minor_grid(2);
Step chart
plot.step(...) holds each value constant until the next sample — the right shape for quantities that change in discrete jumps (counts, levels, states).
import plot;
hours = [0.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0];
replicas = [2.0, 2.0, 4.0, 8.0, 8.0, 6.0, 3.0, 2.0];
chart = plot.step(hours, replicas)
.title("Autoscaled replicas").x_label("hour").y_label("pods").grid(7);
Scatter plot
plot.scatter(...) draws points instead of a connecting line — use it to show the relationship between two variables.
import plot;
chart = plot.scatter(study_hours, exam_scores)
.title("Study hours vs score").x_label("hours").y_label("score")
.grid(6).minor_grid(2);
Bar chart
plot.bar(...) for categorical comparisons. Bars are centred on their x value and the x-domain is padded so the outer bars stay inside the axes.
import plot;
chart = plot.bar([1.0, 2.0, 3.0, 4.0, 5.0], [42.0, 58.0, 33.0, 71.0, 25.0])
.title("Units sold by product").x_label("product").y_label("units").grid(6);
Grouped bars
Add more than one bar series and they are drawn side by side within each slot, so you can compare categories across groups.
import plot;
qs = [1.0, 2.0, 3.0, 4.0];
chart = plot.bar(qs, [18.0, 24.0, 21.0, 30.0]).label("2024")
.add_bar(qs, [22.0, 20.0, 27.0, 34.0]).label("2025")
.title("Quarterly sales").x_label("quarter").y_label("units")
.legend(true).grid(6);
Histogram
plot.histogram(values, bins) buckets raw samples into equal-width bars to show a distribution.
import plot;
samples = [ /* 30 response times in ms */ ];
chart = plot.histogram(samples, 8)
.title("Response-time distribution").x_label("ms").y_label("count").grid(6);
Pie chart
plot.pie(values) — or plot.pie(values, labels) — shows parts of a whole. Each wedge is sized by its share of the total and labelled with its percentage; pass labels and legend(true) to name the categories.
import plot;
chart = plot.pie([35.0, 25.0, 20.0, 15.0, 5.0],
["Rent", "Food", "Transport", "Savings", "Other"])
.title("Monthly budget").legend(true);
Subplots: one figure, many charts
plot.subplots(charts, cols) composes several charts into a single figure laid out as a grid of cols columns. Each chart keeps its own type, title, grid, and axes and is scaled into an equal cell. The result is a normal SVG, so it works with every output path — save_subplots_svg / save_subplots_html to a file, or show_subplots_native to open it in the native window.
import plot;
users = [120.0, 135.0, 148.0, 172.0, 168.0, 205.0, 233.0, 258.0];
visits = [4.0, 9.0, 7.0, 14.0, 20.0, 18.0, 27.0];
sold = [42.0, 58.0, 33.0, 71.0, 25.0];
replicas = [2.0, 2.0, 4.0, 8.0, 8.0, 6.0, 3.0, 2.0];
figure = plot.subplots([
plot.line(users).title("line").grid(4).size(300, 200),
plot.area(visits).title("area").grid(4).size(300, 200),
plot.scatter(study_hours, exam_scores).title("scatter").grid(4).size(300, 200),
plot.bar(sold).title("bar").grid(4).size(300, 200),
plot.step(replicas).title("step").grid(4).size(300, 200),
plot.pie([35.0, 25.0, 20.0, 15.0, 5.0]).title("pie").size(300, 200),
], 3); // 3 columns -> a 3x2 grid, one SVG
Auto-generated from
stdlib/plot.dnbytools/gen_stdlib_docs.py.
record Chart
Methods:
static fn empty(): Chartfn title(value: text): Chart— Return a copy of the chart with its title set. — e.g.plot.svg(plot.line([1.0, 2.0]).title("Sales")).contains("Sales") // 1fn x_label(value: text): Chart— Return a copy with the x-axis label set. — e.g.plot.line([1.0, 2.0]).x_label("time")fn y_label(value: text): Chart— Return a copy with the y-axis label set. — e.g.plot.line([1.0, 2.0]).y_label("value")fn legend(enabled: bool): Chart— Return a copy with the legend shown or hidden. — e.g.plot.line([1.0, 2.0]).label("trend").legend(true)fn size(width: int, height: int): Chart— Return a copy sized towidthxheightpixels (both must be positive). — e.g.plot.svg(plot.line([1.0, 2.0]).size(640, 480)).contains("width=\"640\"") // 1fn grid(cells: int): Chart— Return a copy with a background grid ofcellsmajor divisions per axis — e.g.plot.svg(plot.line([1.0, 2.0]).grid(5)).contains("plot-grid") // 1fn minor_grid(subdivisions: int): Chart— Return a copy that also drawssubdivisionsminor grid lines inside each — e.g.plot.svg(plot.line([1.0, 2.0]).grid(4).minor_grid(5)).contains("plot-grid-minor") // 1fn label(value: text): Chartfn add_line(xs: [real64], ys: [real64]): Chartfn add_line(ys: [real64]): Chartfn add_scatter(xs: [real64], ys: [real64]): Chartfn add_scatter(ys: [real64]): Chartfn add_bar(xs: [real64], ys: [real64]): Chartfn add_bar(ys: [real64]): Chartfn add_area(xs: [real64], ys: [real64]): Chartfn add_area(ys: [real64]): Chartfn add_step(xs: [real64], ys: [real64]): Chartfn add_step(ys: [real64]): Chart
fn empty(): Chart
fn line(xs: [real64], ys: [real64]): Chart
A line chart from paired x/y data.
Example:
plot.svg(plot.line([0.0, 1.0, 2.0], [3.0, 7.0, 5.0])).contains("<svg") // 1
fn line(ys: [real64]): Chart
A line chart from y values, with x taken as the index 0, 1, 2, ...
Example:
plot.svg(plot.line([1.0, 3.0, 2.0])).contains("<svg") // 1
fn scatter(xs: [real64], ys: [real64]): Chart
A scatter chart from paired x/y data.
Example:
plot.svg(plot.scatter([1.0, 2.0], [3.0, 4.0])).contains("<circle") // 1
fn scatter(ys: [real64]): Chart
A scatter chart from y values against their index.
Example:
plot.scatter([3.0, 1.0, 4.0])
fn bar(xs: [real64], ys: [real64]): Chart
A bar chart from paired x/y data.
Example:
plot.svg(plot.bar([1.0, 2.0], [3.0, 5.0])).contains("<rect") // 1
fn bar(ys: [real64]): Chart
A bar chart from y values against their index.
Example:
plot.svg(plot.bar([3.0, 1.0, 2.0])).contains("<rect") // 1
fn area(xs: [real64], ys: [real64]): Chart
An area chart: a line filled down to the baseline.
Example:
plot.svg(plot.area([0.0, 1.0, 2.0], [3.0, 7.0, 5.0])).contains("<polygon") // 1
fn area(ys: [real64]): Chart
An area chart from y values against their index.
Example:
plot.svg(plot.area([3.0, 1.0, 2.0])).contains("<polygon") // 1
fn step(xs: [real64], ys: [real64]): Chart
A step chart: values held constant between samples (like a staircase).
Example:
plot.svg(plot.step([0.0, 1.0, 2.0], [3.0, 7.0, 5.0])).contains("<polyline") // 1
fn step(ys: [real64]): Chart
A step chart from y values against their index.
Example:
plot.svg(plot.step([3.0, 1.0, 2.0])).contains("<polyline") // 1
fn pie(values: [real64]): Chart
A pie chart. Each value becomes a wedge sized by its share of the total and coloured from the palette; wedges are labelled with their percentage.
Example:
plot.svg(plot.pie([3.0, 5.0, 2.0])).contains("<path") // 1
fn pie(values: [real64], labels: [text]): Chart
A pie chart with a category name for each wedge (shown in the legend).
Example:
plot.svg(plot.pie([3.0, 5.0], ["a", "b"]).legend(true)).contains("<path") // 1
fn histogram(values: [real64], bins: int): Chart
A histogram: bucket values into bins equal-width bars.
Example:
plot.svg(plot.histogram([1.0, 2.0, 2.0, 3.0], 3)).contains("<svg") // 1
fn histogram(data: stats.Histogram): Chart
Build a bar chart from a validated stats.Histogram. This preserves custom bin edges and lets data workflows compute/count once in stats, then render the same result in scripts and notebooks.
Example:
plot.svg(plot.histogram(stats.histogram([1.0, 2.0, 3.0], 2).value_or(stats.empty_histogram()))).contains("<svg") // 1
fn use_backend(name: text): unit
Select the display backend for show. Supported MVP backends are:
- "none": return a clear unsupported/headless error;
- "svg": return the deterministic SVG text;
- "html": return a deterministic HTML wrapper containing the SVG.
- "native": open a platform-native plot window when the VM supports it.
fn backend(): text
The active backend used by show. Reads DUNE_PLOT_BACKEND if set, otherwise the value last passed to use_backend.
Example:
plot.use_backend("svg"); plot.backend() // "svg"
fn available_backends(): [text]
The backend names show understands.
Example:
plot.available_backends().len() // 4
fn svg(chart: Chart): text
Render chart to deterministic standalone SVG text.
Example:
plot.svg(plot.line([1.0, 2.0])).starts_with("<svg") // 1
fn subplots(charts: [Chart], cols: int): text
Arrange several charts into one SVG as a grid of cols columns (rows are filled left-to-right, top-to-bottom). Each chart keeps its own size and is scaled into an equal cell taken from the first chart's dimensions.
Example:
plot.subplots([plot.line([1.0, 2.0]), plot.bar([3.0, 1.0])], 2).contains("<svg") // 1
fn html(chart: Chart): text
Render chart to a deterministic standalone HTML document wrapping the SVG.
Example:
plot.html(plot.line([1.0, 2.0])).contains("<!doctype") // 1
fn save_svg(chart: Chart, path: text): outcome.Outcome<text, text>
Write chart as SVG to path, returning an Outcome for the write.
Example:
plot.save_svg(plot.line([1.0, 2.0]), "chart.svg").is_done() // 1
fn save_html(chart: Chart, path: text): outcome.Outcome<text, text>
Write chart as an HTML document to path, returning an Outcome.
Example:
plot.save_html(plot.line([1.0, 2.0]), "chart.html").is_done() // 1
fn show_native(chart: Chart): outcome.Outcome<text, text>
Open chart in a native plot window where the VM supports it; returns an Outcome describing whether the window opened (a headless-safe error if not).
fn subplots_html(charts: [Chart], cols: int): text
Wrap a grid of charts in a standalone HTML document.
Example:
plot.subplots_html([plot.line([1.0, 2.0])], 1).contains("<!doctype") // 1
fn save_subplots_svg(charts: [Chart], cols: int, path: text): outcome.Outcome<text, text>
Write a grid of charts as SVG to path.
Example:
plot.save_subplots_svg([plot.line([1.0, 2.0]), plot.bar([2.0, 1.0])], 2, "grid.svg").is_done() // 1
fn save_subplots_html(charts: [Chart], cols: int, path: text): outcome.Outcome<text, text>
Write a grid of charts as an HTML document to path.
Example:
plot.save_subplots_html([plot.line([1.0, 2.0])], 1, "grid.html").is_done() // 1
fn show_subplots_native(charts: [Chart], cols: int): outcome.Outcome<text, text>
Open a grid of charts in the native plot window where the VM supports it. The window renders the composed SVG, so subplots display exactly as they save.