ruviz
High-performance 2D plotting library for Rust combining matplotlib's ease-of-use with Makie's performance.
Release Notes
Package Surfaces
- Rust crate:
ruviz - GPUI adapter crate:
ruviz-gpui - Raw Rust wasm bridge:
ruviz-web - Python package:
ruvizinpython/ - npm package:
ruvizinpackages/ruviz-web/
Release-facing media and generated docs now use the documented layout in
docs/BUILD_OUTPUTS.md. Regenerate the full release docs
surface with make release-docs on the release docs branch.
Quick Start
use *;
let x: = .map.collect;
let y: = x.iter.map.collect;
new
.line
.title
.xlabel
.ylabel
.save?;
Need typeset math labels? See Typst Text Mode below.

Features
🛡️ Safety & Quality
- Zero unsafe in public API
- Strong type system prevents runtime errors
- Comprehensive error handling with
Resulttypes - Memory-safe by design
📊 Plot Types
Basic: Line, Scatter, Bar, Histogram, Box Plot, Heatmap Distribution: Violin, KDE, ECDF Composition: Pie, Donut Continuous: Contour Polar: Polar Plot, Radar Chart Error: Error Bars (symmetric/asymmetric)
🎨 Publication Quality
- High-DPI export: 72, 96, 300, 600 DPI for print
- Multiple formats: PNG, SVG, and PDF (with the
pdffeature) - Professional themes: Light, Dark, Publication, Seaborn-style
- Custom styling: Colors, fonts, markers, line styles
- International text: Full UTF-8 support (Japanese, Chinese, Korean, etc.) with cosmic-text
⚡ Advanced Features
- Simple API: One-liner functions for quick plotting
- Parallel rendering: Multi-threaded for large datasets (rayon)
- GPU acceleration: Optional wgpu backend (experimental)
- Interactive plots: Optional desktop window integration on Linux, macOS, and Windows
- Mixed-coordinate insets: Embed polar, pie, and radar plots inside Cartesian figures
- Browser runtime: Experimental
ruviz-webadapter andruviznpm SDK forwasm32canvas rendering - Animation: GIF export with
record!macro and easing functions - Cross-platform: Linux, macOS, Windows, and experimental browser/wasm targets
Installation
Add to your Cargo.toml:
[]
= "0.4.0"
Feature Flags
Choose features based on your needs:
[]
= { = "0.4.0", = ["parallel", "simd"] }
| Feature | Description | Use When |
|---|---|---|
default |
ndarray + parallel | General use |
parallel |
Multi-threaded rendering | Large datasets |
simd |
Vectorized transforms | Performance-critical |
animation |
GIF animation export | Animated plots |
gpu |
GPU acceleration backend (experimental) | Opt-in GPU rendering |
interactive |
winit window support | Interactive plots |
ndarray_support |
ndarray types | Scientific computing |
nalgebra_support |
nalgebra vectors/matrices | Linear algebra workloads |
polars_support |
DataFrame support | Data analysis |
pdf |
PDF export | Publication output |
typst-math |
Typst text engine for all plot text | Math-heavy publication plots |
full |
Most bundled features (excludes HQ GIF/video extras) | Power users |
For minimal builds: default-features = false
Benchmark note: the current Rust feature-impact study in
docs/benchmarks/rust-feature-impact.md shows
parallel is the main default performance feature to care about, simd is situational, and
gpu should remain opt-in rather than a default build choice.
Experimental WASM Support
The core crate now compiles for wasm32-unknown-unknown with in-memory output helpers such as
Plot::render_png_bytes(), Plot::render_to_svg(), and Image::encode_png().
For browser interactivity, use the companion Rust bridge crate in
crates/ruviz-web and the public JS/TS SDK package
ruviz. The reference browser demo lives in
demo/web. Native file-path export helpers remain desktop-only.
Note:
ruvizautomatically registers a bundled browser fallback font for canvas sessions.- Custom browser fonts can still be added explicitly via
ruviz::render::register_font_bytes(...). - The current browser adapter provides main-thread canvas and OffscreenCanvas worker sessions, plus
web_runtime_capabilities()for feature diagnostics. - The Vite demo includes direct wasm export, main-thread interactivity, worker interactivity, and temporal signal playback plus Observable-driven updates.
- The JS workspace is Bun-first. Use
bun install,bun run build:web, andbun run test:webfrom the repo root for browser package and demo work.
Python Bindings
The repo now includes a mixed Python package in python built with uv, maturin,
and pyO3.
The Python package exposes a fluent ruviz.plot() builder for static export and uses the browser
runtime for notebook widgets. Outside Jupyter, plot.show() uses the native interactive window.
Standalone MkDocs docs and runnable examples live under python/docs and
python/examples.
Web SDK Docs
The browser-first JS/TS SDK in packages/ruviz-web now ships with
runtime examples under packages/ruviz-web/examples and a
standalone VitePress docs site under packages/ruviz-web/docs.
Typst Text Mode
Enable Typst text rendering:
[]
= { = "0.4.0", = ["typst-math"] }
Use .typst(true) on a plot to render all static text surfaces (titles, axis labels, ticks,
legend labels, and annotations) through Typst:
use *;
let x: = .map.collect;
let y: = x.iter.map.collect;
new
.line
.title
.xlabel
.ylabel
.typst
.save?;
Notes:
- Invalid Typst snippets fail render/export with a
TypstError. .typst(true)is only available whentypst-mathis enabled at compile time.- Without
typst-math, the compiler reports:
error[E0599]: no method named `typst` found for struct `ruviz::core::Plot` in the current scope
- If you select the text engine directly,
TextEngineMode::Typstis also unavailable withouttypst-math, and the compiler reports:
error[E0599]: no variant or associated item named `Typst` found for enum `TextEngineMode` in the current scope
- If Typst is optional in your own crate, define and forward a local feature first:
[]
= { = "0.4.0", = false }
[]
= []
= ["ruviz/typst-math"]
- Then guard the call with your crate feature:
use *;
let mut plot = new
.line
.title
.xlabel
.ylabel;
plot.save?;
- Migration:
.latex(true)has been removed; use.typst(true)instead. - Typst text in PNG output is rasterized at native output scale (1x).
- For maximum text sharpness, prefer higher DPI (for example
.dpi(300)) or vector export (.export_svg(...)/.save_pdf(...)). - DPI changes output density, not the intended physical size of fonts, strokes, markers, or layout spacing.
- Prefer
.size(width_in, height_in)when you care about physical figure size..size_px(width, height)is a convenience that maps pixels through the 100-DPI reference size before final output DPI is applied. - Ticks are enabled by default and render inward on all four sides. Use
.ticks(false)to hide tick marks and tick labels while keeping the frame and axis titles. - Migration: if you want the older bottom/left-only tick appearance, call
.ticks_bottom_left(). - Migration: if you previously relied on high-DPI exports making lines, markers, or text look larger, set those sizes explicitly instead of relying on DPI.
Tick customization:
use *;
new
.line
.tick_direction_inout
.ticks_bottom_left
.show_top_ticks
.show_right_ticks
.save?;
Examples
Basic Line Plot
use *;
let x = vec!;
let y = vec!;
new
.line
.title
.save?;
Multi-Series with Styling
use *;
let x = vec!;
new
.line
.label
.line
.label
.line
.label
.title
.xlabel
.ylabel
.theme
.save?;
Grouped Series with Shared Styling
use *;
let x = vec!;
let a = vec!;
let b = vec!;
let baseline = vec!;
new
.group
.line
.label
.legend
.save?;
Subplots
use *;
let plot1 = new.line.title.end_series;
let plot2 = new.scatter.title.end_series;
let plot3 = new.bar.title.end_series;
let data = vec!;
let plot4 = new
.histogram
.title
.end_series;
subplots?
.suptitle
.subplot?
.subplot?
.subplot?
.subplot?
.save?;
Large Dataset
use *;
// 100,000-point PNG export
let x: = .map.collect;
let y: = x.iter.map.collect;
new
.line
.title
.save?;
Animation
Enable the animation feature for this example:
[]
= { = "0.4.0", = ["animation"] }
use *;
use RecordConfig;
use record;
let x: = .map.collect;
let config = new.max_resolution.framerate;
record!?;

Interactive And Animation Example Catalog
Interactive window examples:
Default interactive window controls:
Mouse wheel: zoom in/out under the cursorLeft click + drag: panRight click: open the context menuRight click + drag: box zoomEscape: close the menu or reset the viewCmd/Ctrl+S: save the current view as PNGCmd/Ctrl+C: copy the current view as an image
The standalone interactive window and the ruviz-gpui adapter are supported on
Linux, macOS, and Windows. On Windows, the recommended CI/native target is
x86_64-pc-windows-msvc.
The built-in context menu includes Reset View, Set Current View As Home,
Go To Home View, Save PNG..., Copy Image, Copy Cursor Coordinates, and
Copy Visible Bounds. You can extend it with custom items through
InteractiveWindowBuilder::context_menu(...) and
InteractiveWindowBuilder::on_context_menu_action(...).
Animation export examples:
Use the interactive examples when you want zoom/pan exploration in a window. Use the
animation examples when you want rendered GIF output with the record! macro and easing
helpers.
Typst Text Example
Run:
Documentation
- User Guide - Comprehensive tutorials and examples
- API Documentation - Complete API reference
- Gallery - Visual examples showcase
- Migration from matplotlib - For Python users
- Migration from seaborn - Statistical plots
- Performance Guide - Optimization techniques
- Large Dataset Benchmarks - Cross-runtime PNG rendering results for Rust, Python, wasm, and matplotlib
- Rust Feature Impact Benchmarks - Rust-only feature-flag study for render and save backend performance
Why ruviz?
Rust's plotting ecosystem has several options, but each has trade-offs:
| Library | Approach | Limitation |
|---|---|---|
| plotters | Low-level drawing API | Verbose, requires boilerplate for common plots |
| plotly.rs | JavaScript bindings | Requires JS runtime, web-focused |
| plotpy | Python/matplotlib wrapper | Requires Python installed |
ruviz fills the gap with:
- High-level API: matplotlib-style
Plot::new().line().title().save()- no boilerplate - Pure Rust: No Python, JavaScript, or external runtime needed
- Built-in plot types: 15+ plot types out of the box (violin, KDE, radar, etc.)
- Publication quality: Professional themes and high-DPI export
// plotters: ~30 lines for a simple line plot
// ruviz: 4 lines
new
.line
.title
.save?;
Contributing
Contributions welcome! See CONTRIBUTING.md for setup, testing, and pull request guidelines.
Development
# Clone repository
# Setup pre-commit hooks (recommended)
# Run code quality checks
# Run tests
# Run examples
# Run benchmarks
The pre-commit hooks will automatically run cargo fmt --check, cargo clippy, oxfmt --check, and oxlint --deny-warnings before each commit.
Roadmap
- Core plot types (line, scatter, bar, histogram, boxplot, heatmap)
- Parallel rendering
- SIMD optimization
- GPU acceleration (experimental)
- Professional themes
- Subplots and multi-panel figures
- Distribution plots: Violin, KDE, ECDF
- Composition plots: Pie, Donut
- Continuous plots: Contour
- Polar plots: Polar, Radar
- Error bars
- SVG export
- Experimental interactive window support
- High-level APIs for area, hexbin, step, and stem plots
- High-level APIs for regression and composite plots
- Stabilize the interactive zoom/pan workflow
- 3D plotting (v1.0+)
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE or http://opensource.org/licenses/MIT)
at your option.
Acknowledgments
- Inspired by matplotlib, seaborn, and Makie.jl
- Built with tiny-skia for rendering
- Text rendering by cosmic-text
- Thanks to the Rust community for excellent crates and feedback
Status: v0.4.0 - Early development, API may change. Production use at your own risk.
Support: Open an issue or start a discussion