palette

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package palette provides colours and colour sequences for charts.

The default qualitative sequence is Okabe-Ito, which is designed to remain distinguishable under the common forms of colour vision deficiency. Charts should also encode series redundantly (dash patterns, marker shapes) — colour alone is never sufficient — but starting from a safe palette costs nothing.

Index

Constants

This section is empty.

Variables

View Source
var (
	Blue      = ir.RGB(0x00, 0x72, 0xB2)
	Orange    = ir.RGB(0xE6, 0x9F, 0x00)
	SkyBlue   = ir.RGB(0x56, 0xB4, 0xE9)
	Green     = ir.RGB(0x00, 0x9E, 0x73)
	Yellow    = ir.RGB(0xF0, 0xE4, 0x42)
	Vermilion = ir.RGB(0xD5, 0x5E, 0x00)
	Purple    = ir.RGB(0xCC, 0x79, 0xA7)
	Black     = ir.RGB(0x00, 0x00, 0x00)
	White     = ir.RGB(0xFF, 0xFF, 0xFF)
	Red       = ir.RGB(0xD5, 0x5E, 0x00)
	Gray      = ir.RGB(0x88, 0x88, 0x88)
)

Named colours used by the examples and the default theme.

View Source
var BlueOrange = Ramp{
	ir.RGB(0x05, 0x30, 0x61),
	Blue,
	ir.RGB(0x92, 0xC5, 0xDE),
	ir.RGB(0xF2, 0xF2, 0xF2),
	ir.RGB(0xF4, 0xA5, 0x82),
	Vermilion,
	ir.RGB(0x7F, 0x27, 0x04),
}

BlueOrange is the default diverging ramp: blue at one end, vermilion at the other, a near-neutral light grey in the middle.

Blue against orange is the one hue contrast that survives every common form of colour vision deficiency, which is why both ends are taken from Okabe-Ito rather than from the red/green pairing diverging ramps usually reach for.

View Source
var Blues = Ramp{
	ir.RGB(0xF7, 0xFB, 0xFF),
	ir.RGB(0xDE, 0xEB, 0xF7),
	ir.RGB(0xC6, 0xDB, 0xEF),
	ir.RGB(0x9E, 0xCA, 0xE1),
	ir.RGB(0x6B, 0xAE, 0xD6),
	ir.RGB(0x42, 0x92, 0xC6),
	ir.RGB(0x21, 0x71, 0xB5),
	ir.RGB(0x08, 0x51, 0x9C),
	ir.RGB(0x08, 0x30, 0x6B),
}

Blues is a single-hue sequential ramp, light to dark. Use it when the chart already carries a hue meaning and a second one would compete.

ColorBrewer 2.0, Brewer & Harrower.

View Source
var Cividis = Ramp{
	ir.RGB(0x00, 0x20, 0x4D),
	ir.RGB(0x00, 0x30, 0x6F),
	ir.RGB(0x39, 0x48, 0x6B),
	ir.RGB(0x57, 0x5D, 0x6D),
	ir.RGB(0x70, 0x71, 0x73),
	ir.RGB(0x8A, 0x87, 0x79),
	ir.RGB(0xA6, 0x9D, 0x75),
	ir.RGB(0xC4, 0xB5, 0x6C),
	ir.RGB(0xE4, 0xCF, 0x5B),
	ir.RGB(0xFF, 0xEA, 0x46),
}

Cividis is a sequential ramp optimised so that readers with and without deuteranomaly see the same ordering of colours, not merely a distinguishable one.

Nuñez, Anderton & Renslow, "Optimizing colormaps with consideration for color vision deficiency" (PLOS ONE, 2018).

View Source
var Default = OkabeIto

Default is the palette figure uses when a theme does not override it.

View Source
var DefaultRamp = Viridis

DefaultRamp is the sequential ramp a colour scale uses when none is given.

View Source
var Greys = Ramp{
	ir.RGB(0xF7, 0xF7, 0xF7),
	ir.RGB(0xD9, 0xD9, 0xD9),
	ir.RGB(0xBD, 0xBD, 0xBD),
	ir.RGB(0x96, 0x96, 0x96),
	ir.RGB(0x73, 0x73, 0x73),
	ir.RGB(0x52, 0x52, 0x52),
	ir.RGB(0x25, 0x25, 0x25),
}

Greys is a neutral sequential ramp, light to dark.

View Source
var Magma = Ramp{
	ir.RGB(0x00, 0x00, 0x04),
	ir.RGB(0x18, 0x0F, 0x3D),
	ir.RGB(0x44, 0x0F, 0x76),
	ir.RGB(0x72, 0x1F, 0x81),
	ir.RGB(0x9E, 0x2F, 0x7F),
	ir.RGB(0xCD, 0x40, 0x71),
	ir.RGB(0xF1, 0x60, 0x5D),
	ir.RGB(0xFD, 0x96, 0x68),
	ir.RGB(0xFE, 0xC9, 0x8D),
	ir.RGB(0xFC, 0xFD, 0xBF),
}

Magma is a sequential ramp from black through purple and red to near-white. Like Viridis it is perceptually uniform and monotone in lightness.

View Source
var OkabeIto = Qualitative{
	Blue,
	Vermilion,
	Green,
	Yellow,
	SkyBlue,
	Orange,
	Purple,
	ir.RGB(0x00, 0x00, 0x00),
}

OkabeIto is the default colourblind-safe qualitative palette.

Okabe & Ito, "Color Universal Design" (2008).

View Source
var PurpleGreen = Ramp{
	ir.RGB(0x40, 0x00, 0x4B),
	ir.RGB(0x9A, 0x70, 0xAB),
	ir.RGB(0xE7, 0xD4, 0xE8),
	ir.RGB(0xF7, 0xF7, 0xF7),
	ir.RGB(0xD9, 0xF0, 0xD3),
	ir.RGB(0x5A, 0xAE, 0x61),
	ir.RGB(0x00, 0x44, 0x1B),
}

PurpleGreen is a second diverging ramp, for when a chart already spends blue or orange on something else. Purple against green is the next safest pairing after blue against orange.

View Source
var Viridis = Ramp{
	ir.RGB(0x44, 0x01, 0x54),
	ir.RGB(0x48, 0x28, 0x78),
	ir.RGB(0x3E, 0x4A, 0x89),
	ir.RGB(0x31, 0x68, 0x8E),
	ir.RGB(0x26, 0x82, 0x8E),
	ir.RGB(0x1F, 0x9E, 0x89),
	ir.RGB(0x35, 0xB7, 0x79),
	ir.RGB(0x6D, 0xCD, 0x59),
	ir.RGB(0xB4, 0xDE, 0x2C),
	ir.RGB(0xFD, 0xE7, 0x25),
}

Viridis is the default sequential ramp: perceptually uniform, monotone in lightness — so it survives being printed in greyscale — and legible under every common form of colour vision deficiency.

Smith & van der Walt, matplotlib (2015).

Functions

func Lerp

func Lerp(a, b ir.Color, t float64) ir.Color

Lerp blends a into b by t in [0, 1], interpolating the colour channels in linear light and the alpha channel directly.

Alpha is not gamma-encoded, so it interpolates as it is. The colours are non-premultiplied, which means blending two colours of different alpha interpolates the *stated* colour rather than the composited one — for a colour ramp, where the anchors are normally opaque, that is what a caller expects.

func Luminance

func Luminance(c ir.Color) float64

Luminance is the relative luminance of c, from 0 for black to 1 for white.

It is WCAG's definition, which is the one a contrast decision is made against: the channels are decoded to linear light and weighted for the eye's sensitivity to each, so that a saturated yellow reads as light and a saturated blue as dark, which is what a reader sees and what averaging the encoded bytes would get wrong. It is the same decode Lerp blends through, for the same reason.

Alpha is ignored: a translucent fill is composited over something this package cannot see, so its luminance is the luminance of the colour it states. A caller who knows what is behind it can blend the two with Lerp first.

func QualitativeName

func QualitativeName(q Qualitative) (string, bool)

QualitativeName reports the name a qualitative palette is registered under. Like RampName it compares colours rather than identity, so a copy is still recognised. ok is false for a palette nobody registered.

func QualitativeNames

func QualitativeNames() []string

QualitativeNames lists the registered qualitative palettes, sorted.

func RampName

func RampName(r Ramp) (string, bool)

RampName reports the name a ramp is registered under.

It compares colours rather than identity, so a ramp that was copied — which is what Ramp.Reverse and any slice expression produce — is still recognised. ok is false for a ramp nobody registered.

func RampNames

func RampNames() []string

RampNames lists the registered ramps, sorted, so that a caller iterating them gets the same order every time.

func RegisterQualitative

func RegisterQualitative(name string, q Qualitative)

RegisterQualitative adds a qualitative palette under a name, replacing any palette already there. It is how a third-party palette becomes reachable from a spec or a config file.

func RegisterRamp

func RegisterRamp(name string, r Ramp)

RegisterRamp adds a ramp under a name, replacing any ramp already there. It is how a third-party ramp becomes reachable from a spec or a config file.

Types

type Qualitative

type Qualitative []ir.Color

Qualitative is a sequence of categorical colours.

func QualitativeByName

func QualitativeByName(name string) (Qualitative, bool)

QualitativeByName looks up a registered qualitative palette.

func (Qualitative) At

func (q Qualitative) At(i int) ir.Color

At returns the i'th colour, wrapping around. A chart with more series than the palette has colours is a chart that needs a different encoding, but wrapping is better than panicking.

type Ramp

type Ramp []ir.Color

Ramp is a continuous colour ramp: anchor colours spaced evenly over [0, 1] and interpolated between.

Interpolation happens in linear light, not in sRGB byte space. Averaging two gamma-encoded bytes darkens the midpoint of a ramp by roughly 20% — a band that reads as a seam across an otherwise smooth gradient. gg composites in linear space for the same reason, so a ramp evaluated here and a gradient rasterised there agree.

func RampByName

func RampByName(name string) (Ramp, bool)

RampByName looks up a registered ramp.

func (Ramp) At

func (r Ramp) At(t float64) ir.Color

At returns the colour at position t, clamped to [0, 1]. An empty Ramp returns ir.Transparent; a one-colour Ramp is constant.

func (Ramp) Reverse

func (r Ramp) Reverse() Ramp

Reverse returns the ramp running the other way. It allocates; a ramp is a handful of colours, and sharing the backing array would let one caller's reversal mutate another's palette.

Jump to

Keyboard shortcuts

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