Skip to main content

Your First Plot

This guide walks you through creating a scatter plot using both the Rust library API and the CLI. By the end, you’ll understand the basic workflow for generating publication-ready visualizations.

Using the Library

1

Create a new Rust project

cargo new my-plot-project
cd my-plot-project
Add Kuva to Cargo.toml:
[dependencies]
kuva = "0.1"
2

Build a scatter plot

Replace the contents of src/main.rs with:
use kuva::prelude::*;

fn main() {
    // Define your data as (x, y) tuples
    let data = vec![
        (1.0_f64, 2.0),
        (3.0, 5.0),
        (5.0, 4.0),
        (7.0, 8.0),
        (9.0, 7.0),
    ];

    // Build the plot using the builder pattern
    let plot = ScatterPlot::new()
        .with_data(data)
        .with_color("steelblue")
        .with_size(5.0)
        .with_legend("Sample Data");

    // Convert to Plot enum and collect into Vec
    let plots: Vec<Plot> = vec![plot.into()];

    // Create layout with auto-computed axis ranges
    let layout = Layout::auto_from_plots(&plots)
        .with_title("My First Plot")
        .with_x_label("X Axis")
        .with_y_label("Y Axis");

    // Render to SVG
    let svg = render_to_svg(plots, layout);
    
    // Write to file
    std::fs::write("my_plot.svg", svg).unwrap();
    
    println!("Plot saved to my_plot.svg");
}
3

Run and view the output

cargo run
This creates my_plot.svg in your project directory. Open it in a web browser or any SVG viewer.
What just happened? You created a ScatterPlot with builder methods, converted it to the Plot enum, configured a Layout, and rendered to SVG in one line with render_to_svg().

Understanding the Pipeline

Every Kuva plot follows this four-stage pipeline:
1. Build Plot      2. Collect       3. Layout        4. Render
ScatterPlot::new() → vec![plot.into()] → Layout::auto_from_plots() → render_to_svg()

1. Build the Plot

Each plot type has a new() constructor and builder methods:
let plot = ScatterPlot::new()
    .with_data(data)           // Required: your data points
    .with_color("steelblue")   // Optional: point color
    .with_size(5.0)            // Optional: point size
    .with_legend("Label");     // Optional: legend entry
Builder methods are chainable and return Self, allowing fluent configuration.

2. Collect into Plot Enum

The Plot enum unifies all 25 plot types. Convert using .into():
let plots: Vec<Plot> = vec![plot.into()];
This allows mixing plot types on one canvas:
let scatter = ScatterPlot::new().with_data(data1);
let line = LinePlot::new().with_data(data2);

let plots: Vec<Plot> = vec![scatter.into(), line.into()];

3. Configure Layout

Layout controls axis ranges, labels, titles, grid, and more:
let layout = Layout::auto_from_plots(&plots)  // Auto-compute ranges from data
    .with_title("Title")
    .with_x_label("X")
    .with_y_label("Y")
    .with_x_range(0.0, 10.0)    // Optional: override auto range
    .with_log_y()               // Optional: logarithmic scale
    .with_theme(Theme::Light);  // Optional: apply theme

4. Render to Output

Choose your backend:
let svg = render_to_svg(plots, layout);
std::fs::write("plot.svg", svg).unwrap();

Using the CLI

The CLI provides access to all plot types without writing code.
1

Prepare your data

Create a TSV or CSV file. For example, data.tsv:
x	y
1.0	2.0
3.0	5.0
5.0	4.0
7.0	8.0
9.0	7.0
Kuva auto-detects TSV vs CSV based on file extension and content. Use .tsv for tab-separated, .csv for comma-separated.
2

Generate a plot

kuva scatter data.tsv --x x --y y -o my_plot.svg
This creates a scatter plot by:
  • Reading data.tsv
  • Using column x for X-axis data
  • Using column y for Y-axis data
  • Writing output to my_plot.svg
3

Customize the plot

Add titles, labels, and styling:
kuva scatter data.tsv \
  --x x --y y \
  --title "My First Plot" \
  --x-label "X Axis" \
  --y-label "Y Axis" \
  --color steelblue \
  --size 5 \
  -o my_plot.svg

CLI Input Options

Input file
string
default:"stdin"
Path to TSV/CSV file. Omit to read from stdin:
cat data.tsv | kuva scatter --x 0 --y 1
Column selection
string | number
Select columns by name (--x col_name) or 0-based index (--x 0):
# By name
kuva scatter data.tsv --x temperature --y pressure

# By index (0-based)
kuva scatter data.tsv --x 0 --y 1
Output format
svg | png | pdf
default:"svg to stdout"
Specify output file and format with -o:
kuva scatter data.tsv --x x --y y -o plot.svg   # SVG
kuva scatter data.tsv --x x --y y -o plot.png   # PNG (requires --features png)
kuva scatter data.tsv --x x --y y -o plot.pdf   # PDF (requires --features pdf)
Omit -o to write SVG to stdout (useful for piping).

Terminal Rendering

Render plots directly in your terminal with --terminal:
kuva scatter data.tsv --x x --y y --terminal
This uses Unicode/ASCII art to display the plot without creating a file. Useful for quick data exploration in SSH sessions or scripts.

Common Plot Types

Here are quick examples of other popular plot types:
use kuva::prelude::*;

let data = vec![(1.0, 2.0), (2.0, 3.5), (3.0, 3.0), (4.0, 5.0)];

let plot = LinePlot::new()
    .with_data(data)
    .with_color("crimson")
    .with_width(2.0);

let plots = vec![plot.into()];
let svg = render_to_svg(plots, Layout::auto_from_plots(&plots));

CLI Examples

kuva line data.tsv --x time --y value --color crimson -o line.svg

Adding Multiple Plot Series

Overlay multiple datasets on one plot:
use kuva::prelude::*;

let data1 = vec![(1.0, 2.0), (2.0, 3.0), (3.0, 4.0)];
let data2 = vec![(1.0, 1.5), (2.0, 2.8), (3.0, 3.2)];

let plot1 = ScatterPlot::new()
    .with_data(data1)
    .with_color("steelblue")
    .with_legend("Series A");

let plot2 = ScatterPlot::new()
    .with_data(data2)
    .with_color("crimson")
    .with_legend("Series B");

let plots: Vec<Plot> = vec![plot1.into(), plot2.into()];
let layout = Layout::auto_from_plots(&plots)
    .with_title("Multiple Series")
    .with_x_label("X")
    .with_y_label("Y");

let svg = render_to_svg(plots, layout);
The layout automatically expands axis ranges to fit all series, and the legend appears if any plot has .with_legend() specified.

Styling and Themes

Apply pre-built themes for consistent styling:
use kuva::prelude::*;

let layout = Layout::auto_from_plots(&plots)
    .with_theme(Theme::Dark);  // Built-in dark theme
Available themes:
  • Theme::Default — Light background, black text
  • Theme::Dark — Dark background, white text
  • Theme::Light — Minimal light theme
  • Theme::Solarized — Solarized color scheme
Use color palettes for multi-series plots:
let palette = Palette::Viridis;  // Sequential color scale
let palette = Palette::Set1;     // Categorical colors

Error Handling

PNG and PDF rendering can fail if SVG is malformed. Handle errors properly:
use kuva::prelude::*;

let plots = vec![/* ... */];
let layout = Layout::auto_from_plots(&plots);

match render_to_png(plots, layout, 2.0) {
    Ok(png_bytes) => {
        std::fs::write("plot.png", png_bytes).unwrap();
        println!("PNG saved successfully");
    }
    Err(e) => {
        eprintln!("PNG rendering failed: {}", e);
    }
}

Next Steps

Plot Type Reference

Explore all 25 plot types with examples

Layout Configuration

Learn about axis customization, themes, and annotations

CLI Reference

Complete CLI command reference for all plot types

API Documentation

Browse the full Rust API documentation

Tips for Success

Start with auto_from_plots() — Let Kuva compute axis ranges automatically from your data, then override specific settings only when needed.
Use the prelude — Import use kuva::prelude::*; once instead of importing individual types. It includes everything you need for typical use cases.
SVG is portable — Start with SVG output (no features required). It works everywhere and can be converted to other formats later if needed.
Explore examples — The GitHub repository contains 25+ examples in the examples/ directory showing every plot type.

Build docs developers (and LLMs) love