Production-grade Grammar of Graphics for Go.
A pure-Go data visualization library implementing a rigorous, declarative Grammar of Graphics pipeline. Inspired by Hadley Wickham's renowned ggplot2, but architected specifically for Go's type safety and interface-driven engine architecture.
ggplot provides an expressive, composable API for generating complex data visualizations. It decouples data manipulation (Apache Arrow, BigQuery, Memory) from statistical transformations and final rendering, resulting in highly scalable plotting pipelines.
| Capability | Supported Features |
|---|---|
| Geometries | Point, Line, Step, Bar, Col, Histogram, Area, Density, Polygon, Rug, HLine, VLine, Segment, Text, BoxPlot, Smooth, Tile, ErrorBar, Crossbar, Linerange, Pointrange, Curve, Violin, Dotplot, Raster, JitterPoint |
| Statistics | Identity, Bin/Count, Density (KDE), Smooth (LOESS + lm), Summary, BoxPlot (Tukey/range whiskers, notch CI), ViolinY (grouped KDE), DotBin (Wilkinson stacking) |
| Aesthetics | Size, Alpha, Shape, Linetype — per-point and per-group mapping |
| Scales | Linear, Log10, Sqrt, Reverse, Discrete, DateTime, Binned, Size, Alpha, Shape, Linetype, Identity |
| Color Palettes | 60+ built-in palettes — Viridis, ColorBrewer, Tab10, Observable, Seaborn, and more |
| Faceting | Grid (row ~ col, margins, drop), Wrap (NCols, drop), Labellers (Value, Both, Context, custom) |
| Data Backends | Native Memory, Apache Arrow IPC/Parquet, BigQuery SQL pushdown |
| Data Types | Float64, Int64, String, Bool, Timestamp, Date, Time |
| Output | PNG, SVG 1.1, PDF 1.4, HiDPI via WithScale() |
| Theming | 60+ themes — Dashboard, Dark, Classic, Minimal, Observable, Seaborn, Nord, Dracula, and more |
| Annotations | AnnotateText, AnnotateRect, AnnotateSegment, AnnotateArrow, AnnotateLabel — layer-less fixed-coordinate annotations |
| Coordinate Systems | CoordCartesian (viewport zoom), CoordFixed (aspect ratio), CoordFlip, Coord(Polar()) |
Free scales, grid margins, custom labellers, and strip placement:
ggplot.New(ds, aes.X("x"), aes.Y("y")).
Layer(geom.Point()).
FacetWrap("species", facet.FreeY()). // independent Y range per panel
Save(ctx, "facets.png", 900, 300)
ggplot.New(ds, aes.X("x"), aes.Y("y")).
Layer(geom.Point()).
FacetGrid("year", "site", facet.GridMargins(true)). // row/col/corner "All" panels
Save(ctx, "grid.png", 900, 600)Labellers (facet.LabelValue, LabelBoth, LabelContext, Label(fn)), facet.Drop(false) to keep empty panels, facet.GridSpace("free_y") for proportional sizing, and facet.StripBottom() / GridStripLeft() for strip repositioning.
Dual Y-axis derived from the primary via a transform pair, or a mirrored duplicate:
ggplot.New(ds, aes.X("hour"), aes.Y("temp_c")).
Layer(geom.Point(geom.WithColor("steelblue"))).
SecondAxis(scale.SecAxis(
func(c float64) float64 { return c*9/5 + 32 }, // °C → °F
func(f float64) float64 { return (f - 32) * 5 / 9 }, // °F → °C
"Temperature (°F)",
)).
Save(ctx, "dual_axis.png", 800, 500)
// scale.DupAxis("°C") mirrors the primary axis on the right.Per-plot element overrides without authoring a custom theme:
ggplot.New(ds, aes.X("x"), aes.Y("y")).
Layer(geom.Point()).
Theme("default").
ThemeOverride(
theme.StripTextOverride(theme.ElementText{Bold: true, Size: 13}),
).
Save(ctx, "styled.png", 800, 500)geom.Violin, geom.Dotplot, geom.Raster, geom.Curve, geom.Crossbar, geom.Linerange, geom.Pointrange, and geom.JitterPoint. Coordinate systems: CoordCartesian (viewport zoom), CoordFixed (aspect ratio), CoordFlip, and Coord(coord.Polar(...)). Coordinate transforms now dispatch to engine-native math kernels (Arrow compute / SQL functions) instead of scalar fallbacks.
Plot.FacetWrap(col string, opts ...WrapOpt)— was(col, nCols, nRows). Usefacet.NCols(n).Plot.FacetGrid(row, col string, opts ...GridOpt)— replaces the old two-arg form.Plot.SecondaryY→Plot.SecondAxis.coord.TransFuncis now a name-only spec type (math moved to engineMathKernel).
What was new in v0.0.8
Map data columns directly to visual properties — size, transparency, shape, and line style:
ggplot.New(ds, aes.X("x"), aes.Y("y"), aes.Size("magnitude"), aes.Alpha("confidence")).
Layer(geom.Point()).
ScaleSizeArea(). // area-proportional sizing
ScaleAlpha(0.2, 0.9). // opacity range
Save(ctx, "scatter.png", 800, 500)Categorical columns map to distinct shapes or dash patterns:
ggplot.New(ds, aes.X("x"), aes.Y("y"), aes.Shape("species")).
Layer(geom.Point(geom.WithSize(5))).
Save(ctx, "shapes.png", 800, 500)
ggplot.New(ds, aes.X("date"), aes.Y("value"), aes.Linetype("model")).
Layer(geom.Line()).
Save(ctx, "linetypes.png", 800, 500)10 built-in shapes: circle, square, triangle, diamond, triangleDown, plus, cross, star, pentagon, hexagon.
6 built-in linetypes: solid, dashed, dotted, dotdash, longdash, twodash.
All shape names are now exported constants (canvas.ShapeCircle, canvas.ShapeStar, etc.) — no more magic strings.
What was new in v0.0.7
- CIELAB gradient constructors:
colormap.Gradient(),Gradient2(),GradientN()with perceptually uniform interpolation - Theme-aware colour defaults: Separate Color/Fill palettes per theme
- Legend key glyphs: Circle for points, line for smooth, rectangle for bars
- Guide customization:
ColorBarWidth(),ColorBarNBin(),LegendCols() - NA color:
scale.SetNAColor()for missing value display
What was new in v0.0.6
- Temporal data types:
DTypeTimestamp,DTypeDate,DTypeTimeacross all engines - DateTime scale:
scale.DateTimewith auto calendar-aligned ticks - Binned scale:
scale.BinnedwithWithBins(n)andWithBinBreaks(edges) - Out-of-bounds policies:
scale.WithOOB(OOBSquish | OOBCensor) - Opt-in drivers: GPU, CSV, Parquet as blank-import packages
Full changelog: CHANGELOG.md
![]() |
![]() |
| Clifford attractor — 500k points, alpha blending, continuous color scale | Butterfly curve — parametric path with color interpolation |
![]() |
Plot.Annotate() — text, rect, segment, arrow, label |
![]() |
![]() |
CoordFixed(1) — circle stays circular |
CoordCartesian — viewport zoom |
![]() |
![]() |
FacetWrap("species") — shared scales |
FacetWrap("species", FreeY()) — independent Y per panel |
![]() |
![]() |
SecondAxis(SecAxis(°C→°F)) — dual Y-axis |
SecondAxis(DupAxis("°C")) — mirrored axis |
Each image is generated by a self-contained example in
examples/.
go get github.com/TuSKan/ggplotpackage main
import (
"context"
"log"
"github.com/TuSKan/ggplot"
"github.com/TuSKan/ggplot/aes"
"github.com/TuSKan/ggplot/dataset"
"github.com/TuSKan/ggplot/dataset/memory"
"github.com/TuSKan/ggplot/geom"
)
func main() {
ctx := context.Background()
eng := memory.NewEngine(ctx)
ds, err := dataset.NewDataset(eng,
eng.NewFloat64Column("x", []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
eng.NewFloat64Column("y", []float64{2, 4, 5, 4, 6, 8, 7, 9, 10, 11}),
)
if err != nil {
log.Fatalln(err)
}
ggplot.New(ds, aes.X("x"), aes.Y("y")).
Layer(geom.Point(geom.WithSize(5), geom.WithColor("coral"))).
Layer(geom.Smooth()).
Labs(ggplot.Title("Quick Start"), ggplot.XLab("X"), ggplot.YLab("Y")).
Save(ctx, "scatter.png", 800, 500)
}ggplot.New(ds, aes.X("day"), aes.Y("temp")).
Layer(geom.Line(geom.WithColor("seagreen"), geom.WithLineWidth(1.5))).
FacetWrap("season", facet.NCols(2)).
Labs(ggplot.Title("Temperature by Season")).
Theme("dark").
Save(ctx, "facets.png", 900, 600)ggplot.New(ds, aes.X("group"), aes.Y("value")).
Layer(geom.Boxplot(geom.WithFill("#E8E8E8"), geom.WithAlpha(0.8))).
Labs(ggplot.Title("Distribution by Group")).
Theme("classic").
Save(ctx, "boxplot.png", 800, 500)- Declarative composition — Build complex charts by layering geometries and statistics instead of drawing pixels.
- Provider-agnostic engines — Swap out the dataset engine (
memory→arrow→bigquery) without changing plotting code. - Publication-ready output — Anti-aliased 2D rendering powered by
gogpu/gg, saving to PNG, SVG, or PDF at configurable DPI scales. - 60+ built-in themes — From minimal dashboards to editorial typography to neon cyberpunk.
- Parallel rendering — Panel-parallel build and draw pipelines via
errgroupfor multi-facet plots.
ggplot is built around an interface-driven dataset.Table engine. You are not limited to []float64 slices — back your plots with robust columnar frameworks. See DATASET.md for a deep-dive.
- Memory Engine (
dataset/memory) — Lightweight native Go slices. Best for standard web-server rendering. - Arrow Engine (
dataset/arrow) — Apache Arrow backed IPC streams and Parquet datasets. Zero-copy reads. Best for datasets >1M rows. - BigQuery Engine (
dataset/bigquery) — Lazy SQL pushdown execution. Best for massive data warehouses where filtering and aggregation execute on the database before streaming visual aggregates to Go.
ggplot ships 60+ built-in themes. Use .Theme("name") to switch:
p.Theme("dashboard") // clean card-style (default)
p.Theme("dark") // dark background
p.Theme("minimal") // minimal chrome
p.Theme("observable") // Observable-inspired
p.Theme("nord") // Nord palette
p.Theme("cyberpunk") // neon-on-darkSee all themes with theme.AllNames().
| Document | Description |
|---|---|
| DATASET.md | Deep dive into the Engine abstraction, Memory, and Arrow backends |
| ARCHITECTURE.md | Package map, rendering pipeline, design decisions |
| ROADMAP.md | Development plan aligned with the ggplot2 book (3e) |
| BENCHMARK.md | Arrow vs Memory engine performance benchmarks |
| Package | Role |
|---|---|
gogpu/gg |
2D vector rendering with anti-aliased lines, fills, and text |
apache/arrow-go |
Columnar data (zero-copy for IPC/Parquet reads) |
Contributions are welcome!
MIT — see LICENSE.


































