refract

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 9 Imported by: 0

README

A Go gopher holding a prism that splits a white beam into a spectrum of charts

refract

CI Coverage Go Reference

A grammar-driven plotting library for Go: one model, many backends, runs everywhere — built on the GoGPU stack.

Status: pre-alpha. This is milestone v0.1, "it plots". The API is not stable; every release below v1.0.0 may contain breaking changes without a deprecation cycle. See CONCEPT.md for the design and the road ahead.

The name is the thesis: one beam enters a prism, a spectrum comes out. One chart specification enters refract, a spectrum of output formats comes out.

A damped sine over a time axis, dark theme


What it is

One declarative chart specification, rendered through interchangeable backends. The core is pure Go with no dependencies at all — not "no cgo", literally nothing outside the standard library — and emits SVG. Add one module and the same specification renders to PNG and JPEG through gogpu/gg, still with CGO_ENABLED=0.

Dependencies Output
github.com/timzifer/refract stdlib only SVG
github.com/timzifer/refract/backend/gg GoGPU (gg), x/image — zero CGO PNG, JPEG

Raster, PDF, GPU, browser and interactive rendering all live behind the same ir.Backend interface. v0.1 ships the first two; the rest are milestones, not architecture changes.

Install

go get github.com/timzifer/refract               # core: SVG, stdlib only
go get github.com/timzifer/refract/backend/gg    # raster: PNG and JPEG

Go 1.25 or newer (why).

Quick start

package main

import (
	"log"
	"math"

	"github.com/timzifer/refract"
	"github.com/timzifer/refract/geom"
	"github.com/timzifer/refract/palette"
	"github.com/timzifer/refract/scale"
	"github.com/timzifer/refract/theme"
)

func main() {
	xs := make([]float64, 200)
	ys := make([]float64, 200)
	for i := range xs {
		xs[i] = float64(i) / 20
		ys[i] = math.Sin(xs[i])
	}

	p := refract.New(
		refract.Theme(theme.Dark),
		refract.Size(800, 400),
		refract.Title("Signal"),
		refract.YTitle("amplitude"),
	)
	p.X(scale.Linear(scale.Nice()))
	p.Y(scale.Linear(scale.Nice()))
	p.Add(geom.Line(
		refract.Float64Columns(map[string][]float64{"x": xs, "y": ys}),
		geom.X("x"), geom.Y("y"),
		geom.Color(palette.Blue),
		geom.Tension(0.4),
	))

	if err := p.Render(refract.SVG("signal.svg")); err != nil {
		log.Fatal(err)
	}
}

For raster, swap the target — nothing else changes:

import ggbackend "github.com/timzifer/refract/backend/gg"

err := p.Render(ggbackend.PNG("signal.png"))

A runnable version is in examples/signal.

Every figure below is rendered by backend/gg/cmd/gallery and re-checked in CI, so a picture here cannot drift away from the code that produced it.

Three series with a legend Two groups of scattered points
A response time histogram A damped sine on a time axis

What v0.1 does

  • Scales — linear and time. Linear tick placement uses extended Wilkinson (Talbot, Lin & Hanrahan 2010), so axis labels come out round rather than merely evenly spaced. Time ticks step in calendar units.
  • GeomsLine (optionally tension-smoothed, with an explicit gap/interpolate/error policy for missing data), Scatter (six marker shapes), Bar.
  • Chart furniture — axes, grid, tick labels with collision avoidance, chart and axis titles, a legend.
  • Themes — light and dark, with a colourblind-safe (Okabe-Ito) default palette.
  • Data — columnar and batch-oriented. A []float64-backed source is borrowed, never copied.
  • Backends — the built-in SVG emitter, and the gg raster adapter.

Deliberately not in v0.1: faceting, constraint layout, PDF, log and ordinal scales, decimation, Arrow, the JSON spec, interactivity, GPU, browser. Each is a later milestone in CONCEPT.md §14.

How it fits together

   Your spec  ──►  Model  ──►  IR  ──►  Backend  ──►  output
   ─────────      ─────      ────      ───────       ──────
   geoms          scales     ~8        backend/svg    SVG
   scales         layout     drawing   backend/gg     PNG / JPEG
   theme          ticks      ops       (future)       PDF, GPU, browser

The ir.Backend interface is the seam. Geoms never touch a renderer; a renderer never knows what a scale is. That is what lets refract stand on a young, fast-moving graphics stack without being welded to it — the whole gg adapter is about 300 lines (why that matters).

Documentation

  • CONCEPT.md — the design document: motivation, positioning, architecture, roadmap.
  • docs/adr — why the open questions were answered the way they were.
  • CONTRIBUTING.md — building a two-module repository, and how to regenerate golden files and figures.

License

MIT. The core links nothing; backend/gg links only permissively licensed code (gg is MIT, x/image is BSD-3-Clause). That is a requirement rather than a preference — refract must be embeddable by downstream projects under any license.

Documentation

Overview

Package refract turns one declarative chart specification into any output you need — SVG today, raster, PDF, GPU and browser through additional backends — from the same model, with the same geometry.

The core module is pure Go and depends on nothing but the standard library. The built-in SVG backend needs no rendering engine and no font stack, so a server that only wants a chart as SVG links nothing native and nothing young. Raster output lives in a separate module, github.com/timzifer/refract/backend/gg, which is still CGO-free.

Shape of the API

Build a plot, give it scales, add layers, render it to a target:

src := refract.Float64Columns(map[string][]float64{"t": times, "y": values})

p := refract.New(
    refract.Theme(theme.Dark),
    refract.Size(800, 500),
    refract.Title("Signal"),
)
p.X(scale.Time())
p.Y(scale.Linear(scale.Nice()))
p.Add(geom.Line(src, geom.X("t"), geom.Y("y"), geom.Color(palette.Blue)))

err := p.Render(refract.SVG("signal.svg"))

Status

Pre-alpha. Every release below v1.0.0 may contain breaking changes without a deprecation cycle. See CONCEPT.md for the design and the roadmap.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoLayers = errors.New("refract: plot has no layers and no scales")

ErrNoLayers reports a render of a plot with nothing in it. Rendering empty axes is a legitimate thing to want, so this is only returned when there is also no scale configured — that combination is always a mistake.

Functions

func NewTable

func NewTable() *data.Table

NewTable returns an empty table that can mix numeric and time columns. See data.NewTable.

Types

type Backend

type Backend = ir.Backend

Backend is a renderer. See package ir.

type Option

type Option func(*Plot)

Option configures a Plot at construction.

func DPR

func DPR(r float64) Option

DPR sets the device pixel ratio. Backends that rasterize multiply the pixel buffer by it; coordinates stay in device-independent units either way. The default is 1.

func Legend

func Legend(show bool) Option

Legend forces the legend on or off. By default a legend appears once a plot has more than one layer: one series does not need to be told apart from anything.

func Size

func Size(w, h int) Option

Size sets the output size in device-independent pixels. The default is 800x500.

func Theme

func Theme(t themepkg.Theme) Option

Theme sets the visual tokens. The default is [theme.Light].

func Title

func Title(s string) Option

Title sets the chart title.

func XTitle

func XTitle(s string) Option

XTitle sets the horizontal axis title.

func YTitle

func YTitle(s string) Option

YTitle sets the vertical axis title.

type Plot

type Plot struct {
	// contains filtered or unexported fields
}

Plot is a chart specification: size, theme, scales and layers.

A Plot is not safe for concurrent modification. Rendering the same Plot twice is supported and produces the same result, provided the underlying data has not changed.

func New

func New(opts ...Option) *Plot

New creates a Plot.

func (*Plot) Add

func (p *Plot) Add(gs ...geom.Geom) *Plot

Add appends layers, drawn in the order given.

func (*Plot) Render

func (p *Plot) Render(t Target) (err error)

Render draws the plot into t.

It opens the target, lowers the chart into the backend it returns, flushes, and closes the target — so a file target has a complete file on disk when Render returns nil.

func (*Plot) X

func (p *Plot) X(s scale.Scale) *Plot

X sets the horizontal scale. The default is scale.Linear with nicing.

func (*Plot) Y

func (p *Plot) Y(s scale.Scale) *Plot

Y sets the vertical scale. The default is scale.Linear with nicing.

type Source

type Source = data.Source

Source is a columnar data source. See package data.

func Float64Columns

func Float64Columns(cols map[string][]float64) Source

Float64Columns builds a Source over numeric columns, borrowing the slices. See data.Float64Columns.

type Target

type Target = ir.Target

Target is a render destination. See package ir.

func SVG

func SVG(path string, opts ...svg.Option) Target

SVG returns a target writing an SVG document to the named file.

This is the zero-dependency path: it uses the built-in emitter in backend/svg and links no rendering engine.

func SVGWriter

func SVGWriter(w io.Writer, opts ...svg.Option) Target

SVGWriter returns a target writing an SVG document to w.

Directories

Path Synopsis
arrow module
backend
svg
Package svg is refract's built-in, zero-dependency SVG backend.
Package svg is refract's built-in, zero-dependency SVG backend.
gg module
gg/gpu module
window module
Package data is refract's data layer: columnar, batch-oriented access to a table of values.
Package data is refract's data layer: columnar, batch-oriented access to a table of values.
examples
signal command
Command signal renders the chart from CONCEPT.md §13.
Command signal renders the chart from CONCEPT.md §13.
Package geom holds the visual marks a chart is made of.
Package geom holds the visual marks a chart is made of.
internal
fontmetrics
Package fontmetrics answers "how wide is this string" using nothing but the standard library.
Package fontmetrics answers "how wide is this string" using nothing but the standard library.
irtest
Package irtest provides a recording ir.Backend for tests.
Package irtest provides a recording ir.Backend for tests.
svgdiff
Package svgdiff compares two SVG documents as drawings rather than as bytes.
Package svgdiff compares two SVG documents as drawings rather than as bytes.
Package ir defines refract's intermediate representation: a small, backend-agnostic scene description, plus the Backend interface every renderer implements.
Package ir defines refract's intermediate representation: a small, backend-agnostic scene description, plus the Backend interface every renderer implements.
Package layout decides where the plot area, titles and legend go.
Package layout decides where the plot area, titles and legend go.
Package palette provides colours and colour sequences for charts.
Package palette provides colours and colour sequences for charts.
Package render lowers a resolved chart into IR.
Package render lowers a resolved chart into IR.
Package scale maps data values onto visual positions and generates the ticks that label them.
Package scale maps data values onto visual positions and generates the ticks that label them.
Package theme holds the visual tokens a chart is drawn with: colours, fonts, sizes and spacings.
Package theme holds the visual tokens a chart is drawn with: colours, fonts, sizes and spacings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL