Table of Contents

Line Charts

Tips: Try .WithBrowserInteraction() to make your line chart zoomable in the browser. Use .WithSymlogYScale() for data spanning large ranges. Switch themes with .WithTheme(Theme.Nord).

Simple line chart

double[] x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
double[] y = [2.1, 4.5, 3.2, 6.8, 5.1, 7.3, 6.5, 8.9, 7.2, 9.4];

Plt.Create()
    .WithTitle("Sales Trend")
    .WithTheme(Theme.Seaborn)
    .WithSize(800, 500)
    .Plot(x, y, line => { line.Color = Colors.Blue; line.Label = "Revenue"; })
    .Save("chart.svg");

Line chart

Line styles, markers, and width

Customize every aspect of the line via the configure lambda:

Plt.Create()
    .WithTitle("Line Styles")
    .AddSubPlot(1, 1, 1, ax =>
    {
        ax.Plot(x, y1, s =>
        {
            s.Color = Colors.Blue;
            s.LineStyle = LineStyle.Solid;
            s.LineWidth = 2.0;
            s.Label = "Solid (2px)";
        });
        ax.Plot(x, y2, s =>
        {
            s.Color = Colors.Red;
            s.LineStyle = LineStyle.Dashed;
            s.LineWidth = 1.5;
            s.Label = "Dashed";
        });
        ax.Plot(x, y3, s =>
        {
            s.Color = Colors.Green;
            s.LineStyle = LineStyle.DashDot;
            s.LineWidth = 1.0;
            s.Marker = MarkerStyle.Circle;
            s.MarkerSize = 6;
            s.MarkEvery = 2;  // marker every 2nd point
            s.Label = "DashDot + markers";
        });
        ax.WithLegend(LegendPosition.UpperLeft);
    })
    .Save("line_styles.svg");

Smooth interpolation

Fritsch-Carlson cubic spline for smooth curves:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s =>
        {
            s.Smooth = true;
            s.SmoothResolution = 20;  // 20 sub-points per interval
            s.Color = Colors.Purple;
            s.Label = "Smoothed";
        })
        .Scatter(x, y, s =>
        {
            s.Color = Colors.Red;
            s.MarkerSize = 8;
            s.Label = "Data points";
        })
        .WithLegend())
    .Save("smooth_line.svg");

Step functions

Control step placement with DrawStyle:

Plt.Create()
    .WithSize(900, 400)
    .AddSubPlot(1, 3, 1, ax => ax
        .Plot(x, y, s => { s.DrawStyle = DrawStyle.Steps; s.Label = "Steps"; })
        .WithTitle("Steps").WithLegend())
    .AddSubPlot(1, 3, 2, ax => ax
        .Plot(x, y, s => { s.DrawStyle = DrawStyle.StepPre; s.Label = "StepPre"; })
        .WithTitle("StepPre").WithLegend())
    .AddSubPlot(1, 3, 3, ax => ax
        .Plot(x, y, s => { s.DrawStyle = DrawStyle.StepMid; s.Label = "StepMid"; })
        .WithTitle("StepMid").WithLegend())
    .TightLayout()
    .Save("step_styles.svg");

Marker customization

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s =>
        {
            s.Marker = MarkerStyle.Diamond;
            s.MarkerSize = 10;
            s.MarkerFaceColor = Colors.Gold;
            s.MarkerEdgeColor = Colors.DarkRed;
            s.MarkerEdgeWidth = 1.5;
            s.Color = Colors.DarkRed;
            s.Label = "Diamond markers";
        })
        .WithLegend())
    .Save("marker_custom.svg");

All marker shapes (v1.7.2 Phase M)

MarkerStyle exposes 13 shapes, every one drawn by the shared MarkerRenderer:

Shape SVG primitive Notes
Circle <circle> Default. Radius = MarkerSize / 2.
Square <rect> Edge = MarkerSize.
Triangle / TriangleDown / TriangleLeft / TriangleRight <polygon> (3 vertices) Equilateral; orientation as named.
Diamond <polygon> (4 vertices) Square rotated 45°.
Pentagon / Hexagon <polygon> (5 / 6 vertices) Regular N-gon, flat-top-up variant.
Star <polygon> (10 vertices) 5-point star, inner radius ≈ 0.38 × outer (matplotlib parity).
Cross / Plus <line> × 2 Outline-only (no fill). Stroke thickness falls back to MarkerSize / 8 when MarkerEdgeWidth is zero.
None No marker drawn.

Prior to v1.7.2 Phase M, line charts drew every marker as a circle and scatter plots honoured only Square; all other shapes silently collapsed to circles. The shared MarkerRenderer dispatches all shapes uniformly across both series types.

Grid and spine control

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, y, s => s.Label = "Data")
        // Grid styling
        .WithGrid(g => g with
        {
            Color = Colors.LightGray,
            LineStyle = LineStyle.Dotted,
            LineWidth = 0.5,
            Alpha = 0.8
        })
        // Hide top and right spines (matplotlib style)
        .HideTopSpine()
        .HideRightSpine()
        // Axis ranges
        .SetXLim(0, 12)
        .SetYLim(0, 12)
        .SetXLabel("Time (s)")
        .SetYLabel("Amplitude")
        .WithLegend())
    .Save("grid_spines.svg");

Secondary Y-axis

Plot two datasets with different scales on the same axes:

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .Plot(x, temperature, s => { s.Color = Colors.Red; s.Label = "Temperature (°C)"; })
        .SetYLabel("Temperature (°C)")
        .WithSecondaryYAxis(sec =>
        {
            sec.Plot(x, pressure, s => { s.Color = Colors.Blue; s.Label = "Pressure (hPa)"; });
            sec.SetYLabel("Pressure (hPa)");
        })
        .WithLegend())
    .Save("dual_axis.svg");

PropCycler — automatic color + line style cycling

When plotting multiple series, PropCycler automatically assigns distinct colors and line styles:

var cycler = new PropCyclerBuilder()
    .WithColors(Colors.Tab10Blue, Colors.Orange, Colors.Green, Colors.Red)
    .WithLineStyles(LineStyle.Solid, LineStyle.Dashed, LineStyle.Dotted, LineStyle.DashDot)
    .Build();

double[] x = Enumerable.Range(0, 60).Select(i => i * 0.2).ToArray();

Plt.Create()
    .WithTitle("PropCycler — four series, cycling color + line style")
    .WithTheme(Theme.CreateFrom(Theme.Default).WithPropCycler(cycler).Build())
    .AddSubPlot(1, 1, 1, ax =>
    {
        ax.Plot(x, x.Select(v => Math.Sin(v)).ToArray(),       s => s.Label = "sin(x)");
        ax.Plot(x, x.Select(v => Math.Sin(v + 1.0)).ToArray(), s => s.Label = "sin(x+1)");
        ax.Plot(x, x.Select(v => Math.Sin(v + 2.0)).ToArray(), s => s.Label = "sin(x+2)");
        ax.Plot(x, x.Select(v => Math.Sin(v + 3.0)).ToArray(), s => s.Label = "sin(x+3)");
        ax.WithLegend(LegendPosition.UpperRight);
    })
    .TightLayout()
    .Save("prop_cycler.svg");

PropCycler

LTTB downsampling for large datasets

Display 10,000 points as 500 using the Largest-Triangle-Three-Buckets algorithm:

double[] x = Enumerable.Range(0, 10_000).Select(i => (double)i).ToArray();
double[] y = x.Select(v => Math.Sin(v * 0.05) * Math.Exp(-v * 0.0003)).ToArray();

Plt.Create()
    .AddSubPlot(1, 1, 1, ax => ax
        .WithTitle("10 000-point signal (LTTB → 500 display points)")
        .Plot(x, y, line => { line.Label = "Signal"; })
        .WithDownsampling(500))
    .Save("lttb.svg");

LTTB downsampling

Outside legend

Place the legend outside the plot area — the constrained-layout engine reserves margin space automatically:

Plt.Create()
    .WithSize(900, 500)
    .TightLayout()
    .AddSubPlot(1, 1, 1, ax =>
    {
        ax.Plot(x, x.Select(v => Math.Sin(v)).ToArray(), s => s.Label = "sin(x)");
        ax.Plot(x, x.Select(v => Math.Cos(v)).ToArray(), s => s.Label = "cos(x)");
        ax.WithLegend(l => l with { Position = LegendPosition.OutsideRight, Title = "Series" });
    })
    .Save("legend_outside.svg");

Outside legend

Legend customization

ax.WithLegend(l => l with
{
    Position = LegendPosition.UpperRight,
    NCols = 2,               // two-column layout
    FontSize = 10,
    Title = "Metrics",
    TitleFontSize = 12,
    FrameOn = true,
    FancyBox = true,          // rounded corners
    Shadow = true,
    FaceColor = Colors.AliceBlue,
    EdgeColor = Colors.SteelBlue,
    FrameAlpha = 0.9,
    MarkerScale = 1.5,
    LabelSpacing = 0.4,
    ColumnSpacing = 1.5,
});

Fluent API reference — LineSeries

Property Type Default Description
Color Color auto Line color
LineStyle LineStyle Solid Solid, Dashed, Dotted, DashDot
LineWidth double 1.5 Line width in pixels
Marker MarkerStyle none Circle, Square, Triangle, Diamond, Star, Cross, Plus, ...
MarkerSize double 6 Marker diameter
MarkerFaceColor Color auto Marker fill color
MarkerEdgeColor Color auto Marker edge color
MarkerEdgeWidth double 1 Marker edge width
MarkEvery int 1 Show marker every N points
DrawStyle DrawStyle Default Default, Steps, StepPre, StepMid, StepPost
Smooth bool false Fritsch-Carlson cubic interpolation
SmoothResolution int 10 Sub-points per interval when smoothing
Label string none Legend label
Visible bool true Show/hide series
ZOrder int 0 Render order (higher = on top)

Bump / rank chart

Invert the Y axis so rank 1 is at the top. Use SetYLim(n + 0.5, 0.5) to flip the scale.

var rng = new Random(99);
string[] brands = ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"];
int periods = 8;
double[] xPeriods = Enumerable.Range(1, periods).Select(i => (double)i).ToArray();
var palette = new[] { "#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00" };

int[][] ranks = Enumerable.Range(0, brands.Length).Select(_ => new int[periods]).ToArray();
for (int p = 0; p < periods; p++)
{
    int[] perm = Enumerable.Range(1, brands.Length).OrderBy(_ => rng.Next()).ToArray();
    for (int b = 0; b < brands.Length; b++) ranks[b][p] = perm[b];
}

Plt.Create()
    .WithTitle("Bump Chart — Brand Ranking over Time")
    .WithSize(900, 450)
    .AddSubPlot(1, 1, 1, ax =>
    {
        for (int b = 0; b < brands.Length; b++)
        {
            double[] y = ranks[b].Select(r => (double)r).ToArray();
            ax.Plot(xPeriods, y, s => { s.Color = Color.FromHex(palette[b]); s.LineWidth = 3; s.Marker = MarkerStyle.Circle; s.MarkerSize = 10; s.Label = brands[b]; });
        }
        ax.SetYLim(brands.Length + 0.5, 0.5); // invert: rank 1 at top
        ax.SetYLabel("Rank");
        ax.WithLegend();
    })
    .Save("bump_chart.svg");

Bump chart