5 releases (3 breaking)
| new 0.5.0 | Jun 6, 2026 |
|---|---|
| 0.4.0 | Jun 6, 2026 |
| 0.2.0 | Jun 5, 2026 |
| 0.1.1 | Jun 5, 2026 |
| 0.1.0 | Jun 5, 2026 |
#9 in #multi-dimensional-array
29 downloads per month
Used in 7 crates
665KB
13K
SLoC
Rust finally has a plotting library that looks as good as matplotlib — and it's faster.
No Python subprocess. No JavaScript runtime. No system dependencies. Just add plotkit to your Cargo.toml and start making figures that belong in a journal paper.
Quick Start
plotkit::plot(&x, &y)?;
plotkit::title("sin(x)");
plotkit::savefig("plot.png")?;
Three lines. Anti-aliased text, clean axes, and a Tableau-10 color palette out of the box.
Installation
[dependencies]
plotkit = "0.4"
Optional features:
plotkit = { version = "0.4", features = ["svg", "jupyter", "ndarray", "polars"] }
| Feature | Description |
|---|---|
png |
PNG rasterization (enabled by default) |
svg |
SVG vector output |
jupyter |
Inline display in Evcxr notebooks |
ndarray |
Plot directly from ndarray::Array1 |
polars |
Plot directly from Polars Series/DataFrame |
Chart Types
plotkit supports 16 chart types with a matplotlib-familiar API:
| Chart Type | Method | Description |
|---|---|---|
| Line | ax.plot(x, y) |
Connected data points with optional markers |
| Scatter | ax.scatter(x, y) |
Individual data points with colormap support |
| Bar | ax.bar(cats, vals) |
Vertical bars with stacking and grouping |
| Horizontal Bar | ax.barh(cats, vals) |
Horizontal bars |
| Grouped Bar | ax.bar_group(cats, series) |
Side-by-side bars for multiple series |
| Histogram | ax.hist(data, bins) |
Frequency distribution |
| Fill Between | ax.fill_between(x, y1, y2) |
Shaded region between curves |
| Step | ax.step(x, y) |
Staircase function |
| Stem | ax.stem(x, y) |
Lollipop chart |
| Box Plot | ax.boxplot(datasets) |
Statistical box-and-whisker |
| Violin | ax.violin(datasets) |
Kernel density distribution |
| Error Bar | ax.errorbar(x, y) |
Data with uncertainty ranges |
| Heatmap | ax.heatmap(data) |
2D color matrix with colorbar |
| Pie | ax.pie(sizes) |
Proportional wedge chart |
| Contour | ax.contour(x, y, z) |
Iso-line contour plot |
| Filled Contour | ax.contourf(x, y, z) |
Filled contour regions |
Two APIs
pyplot-style (quick scripts)
use plotkit::prelude::*;
let x: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let y: Vec<f64> = x.iter().map(|&v| v.sin()).collect();
plotkit::plot(&x, &y)?;
plotkit::title("sin(x)");
plotkit::xlabel("x");
plotkit::ylabel("y");
plotkit::savefig("sine.png")?;
Figure/Axes (full control)
use plotkit::prelude::*;
use plotkit::FigureExt;
let mut fig = Figure::with_size(1200, 500);
let ax1 = fig.add_subplot(1, 2, 1);
ax1.plot(&x, &sin_y)?.label("sin(x)");
ax1.plot(&x, &cos_y)?.label("cos(x)");
ax1.set_title("Trigonometric Functions");
ax1.set_xlabel("x");
ax1.set_ylabel("y");
ax1.legend();
let ax2 = fig.add_subplot(1, 2, 2);
ax2.scatter(&x, &y)?;
ax2.set_title("Scatter Plot");
fig.save("subplots.png")?;
Twin Axes
Overlay two datasets with independent y-scales:
let mut fig = Figure::with_size(800, 600);
let ax1 = fig.add_subplot(1, 1, 1);
ax1.plot(&time, &temperature)?.label("Temperature");
ax1.set_ylabel("Temperature (°C)");
let ax2 = fig.twinx(0);
ax2.plot(&time, &pressure)?.label("Pressure");
ax2.set_ylabel("Pressure (hPa)");
ax1.legend();
Colormaps & Colorbar
14 built-in colormaps: Viridis, Plasma, Inferno, Magma, Cividis, Turbo, Coolwarm, Spectral, RdYlBu, RdYlGn, PiYG, BrBG, PRGn, RdBu.
ax.heatmap(data)?.colormap(Colormap::Viridis).colorbar(true);
ax.scatter(&x, &y)?.colormap(Colormap::Plasma).c(&values);
Scales & Axis Control
ax.set_xscale(Scale::Log10);
ax.set_yscale(Scale::SymLog { linthresh: 1.0 });
ax.set_xlim(0.1, 1000.0);
ax.set_xticks(&[0.1, 1.0, 10.0, 100.0, 1000.0]);
ax.invert_yaxis();
ax.grid(true);
Annotations
ax.text(2.0, 0.8, "Peak", None);
ax.annotate(
"Important point",
(3.0, 0.5), // data point
(4.5, 0.8), // text position
ArrowStyle::Arrow,
);
Themes
fig.set_theme(Theme::dark()); // Dark background, light text
fig.set_theme(Theme::default()); // Clean white background
Custom themes:
let theme = Theme {
figure_background: Color::rgb(0xF5, 0xF5, 0xF5),
font_family: Some("Helvetica".to_string()),
..Theme::default()
};
fig.set_theme(theme);
DataFrame Integration
Polars
use plotkit::prelude::*;
let df = polars::df! {
"x" => [1.0, 2.0, 3.0, 4.0],
"y" => [1.0, 4.0, 9.0, 16.0],
}?;
ax.plot(df.column("x")?, df.column("y")?)?;
ndarray
use ndarray::Array1;
let x = Array1::linspace(0.0, 10.0, 100);
let y = x.mapv(f64::sin);
ax.plot(&x, &y)?;
Output Formats
| Format | Method | Notes |
|---|---|---|
| PNG | fig.save("out.png") |
CPU-rendered, deterministic |
| SVG | fig.save("out.svg") |
Scalable vector, feature svg |
| Bytes | fig.to_png_bytes() |
For embedding / streaming |
| String | fig.to_svg_string() |
For web / templating |
Architecture
plotkit is a multi-crate workspace:
| Crate | Purpose |
|---|---|
plotkit |
Umbrella crate with pyplot API |
plotkit-core |
Figure, Axes, Artists, Renderer trait |
plotkit-render-skia |
PNG backend (tiny-skia + cosmic-text) |
plotkit-render-svg |
SVG backend |
plotkit-ndarray |
ndarray integration |
plotkit-polars |
Polars integration |
The Renderer trait is the abstraction boundary — core logic never depends on a specific backend. Adding a new output format means implementing one trait.
Performance
Pure-Rust CPU renderer. No GPU, no system dependencies, no surprises.
plotkit compiles to a single static binary with zero runtime dependencies. It renders plots entirely on the CPU using optimized rasterization. There is no OpenGL context to initialize, no system font lookup to fail, and no shared library to go missing on a CI server at 2 AM.
Embedded Inter font (Regular + Bold) ensures deterministic, cross-platform text rendering — the same plot looks identical on Linux, macOS, and Windows.
Roadmap
| Version | Focus |
|---|---|
| v0.5 | WASM support — render in the browser |
| v0.6 | 3D surface plots |
| v0.7 | Animation / frame sequences |
| v1.0 | Stable API, full documentation |
Contributing
Contributions are welcome. Whether it's a bug report, a feature request, or a pull request — we appreciate it.
Please read CONTRIBUTING.md before getting started.
License
Licensed under either of
at your option.
lib.rs:
Core types and rendering logic for the plotkit plotting library.
This crate provides the fundamental types (Figure, Axes, Artist),
the Renderer trait, and all chart rendering logic. It is backend-agnostic —
concrete renderers live in separate crates.
Most users should use the plotkit umbrella crate instead of depending on
this crate directly.
Dependencies
~0.2–3.5MB
~63K SLoC