Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

imagine.color

import imagine

Everything here is re-exported by imagine, so import imagine is enough and the names are called as imagine.*. Importing imagine.color on its own works too and reaches the same definitions.

Color: one RGBA colour, and the conversions between the ways of naming one.

It reads and writes hex, RGB, HSL and HSV, blends against another colour, and reports its own luminance. The named constants are the handful worth not spelling out every time.

Constants

TRANSPARENT

imagine.TRANSPARENT

BLACK

imagine.BLACK: Color

Opaque black.

WHITE

imagine.WHITE: Color

Opaque white.

GRAY

imagine.GRAY: Color

Opaque mid grey.

RED

imagine.RED: Color

Opaque pure red.

GREEN

imagine.GREEN: Color

Opaque pure green.

This is the additive primary, not CSS’s green, which is half as bright. Color.named('green') gives that one.

BLUE

imagine.BLUE: Color

Opaque pure blue.

YELLOW

imagine.YELLOW: Color

Opaque yellow.

CYAN

imagine.CYAN: Color

Opaque cyan.

MAGENTA

imagine.MAGENTA: Color

Opaque magenta.

Classes

Color

class imagine.Color

An 8-bit RGBA colour.

Alpha runs 0 (fully transparent) to 255 (fully opaque), the same convention as CSS, PNG and every modern image format. Colours are immutable: every method that would change one returns a new Color, so a colour held in a variable is safe to pass around and reuse.

import imagine { Color }

var red = Color(255, 0, 0)
var glass = red.with_alpha(128)
var brand = Color.hex('#4f46e5')
Relationship to the colors module

The colour space conversions here are the standard library’s, from [[colors]]; Color is the value type that carries a result around and puts it into a pixel buffer. Anything colors can do to a hexadecimal string can be done to a Color by way of to_hex() and Color.hex().

Three conventions follow from that and are worth knowing:

  • Hue is in degrees and saturation, lightness and value are in percentage points (0-100), exactly as in colors and in CSS. They are not 0-1 ratios.

  • to_hex() returns a leading #, because that is the form you paste into a stylesheet. colors returns the bare form. Both are accepted everywhere either is.

  • A malformed hexadecimal colour or an unknown colour name raises ValueError, which is what colors reports for them, rather than this module’s own ImageError. An argument of the wrong type raises TypeError from the declaration it failed.

  • printable — has a @to_string(), so echo and print() show something useful

  • serializable — has a @to_json(), so it can be handed straight to json.encode()

Fields

FieldTypeDescription
rnumberThe red channel, 0-255.
gnumberThe green channel, 0-255.
bnumberThe blue channel, 0-255.
anumberThe alpha channel, 0 (transparent) to 255 (opaque).

Constructor

imagine.Color(r, g, b, a)

Creates a colour from its channels.

Values outside 0-255 are clamped rather than rejected, and fractional values are rounded.

Parameters

  • r (number) — The red channel.
  • g (number) — The green channel.
  • b (number) — The blue channel.
  • a (?number) — The alpha channel - Default: 255 (opaque).

Color.rgb()

imagine.Color.rgb(r, g, b) -> Color

Creates an opaque colour from red, green and blue channels.

The same as calling the constructor with three arguments; it exists so a call site that also uses Color.hsl() or Color.lab() can name the space it is working in.

Parameters

  • r (number)
  • g (number)
  • b (number)

Returns Color

Color.rgba()

imagine.Color.rgba(r, g, b, a) -> Color

Creates a colour from red, green, blue and alpha channels.

Parameters

  • r (number)
  • g (number)
  • b (number)
  • a (number)

Returns Color

Color.gray()

imagine.Color.gray(value, a) -> Color

Creates a shade of grey.

Parameters

  • value (number) — The brightness, 0 (black) to 255 (white).
  • a (?number) — The alpha channel - Default: 255.

Returns Color

Color.packed()

imagine.Color.packed(value: number) -> Color

Creates a colour from a packed 0xRRGGBBAA integer.

Alpha is in the least significant byte, which is the layout to_packed() produces and the one this module passes around internally.

%> Color.packed(0xFF0000FF).to_hex()
'#ff0000'

Parameters

  • value (number)

Returns Color

Color.hex()

imagine.Color.hex(spec) -> Color

Creates a colour from a CSS-style hexadecimal string.

All four CSS lengths are accepted, with or without the leading #, in either case: #rgb, #rgba, #rrggbb and #rrggbbaa. The short forms double each digit, so #f0a is #ff00aa.

Parameters

  • spec (string)

Returns Color

Raises ValueError When spec is not a hexadecimal colour.

Color.named()

imagine.Color.named(name) -> Color

Looks up one of the CSS named colours.

Matching ignores case, spaces, hyphens and underscores, so 'Dark Sea Green' and 'darkseagreen' are the same colour. The full CSS Color Level 4 list is available, including both spellings of the greys and transparent.

Parameters

  • name (string)

Returns Color

Raises ValueError When name is not a CSS colour name.

Color.hsl()

imagine.Color.hsl(h, s, l, a) -> Color

Creates a colour from hue, saturation and lightness.

Parameters

  • h (number) — The hue in degrees. Wraps, so 400 is the same as 40.
  • s (number) — The saturation in percentage points, 0-100.
  • l (number) — The lightness in percentage points, 0 (black) to 100 (white).
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.hsv()

imagine.Color.hsv(h, s, v, a) -> Color

Creates a colour from hue, saturation and value (also called HSB).

Parameters

  • h (number) — The hue in degrees.
  • s (number) — The saturation in percentage points, 0-100.
  • v (number) — The value in percentage points, 0-100.
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.hwb()

imagine.Color.hwb(h, w, b, a) -> Color

Creates a colour from hue, whiteness and blackness.

Parameters

  • h (number) — The hue in degrees.
  • w (number) — The whiteness in percentage points, 0-100.
  • b (number) — The blackness in percentage points, 0-100.
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.cmyk()

imagine.Color.cmyk(c, m, y, k, a) -> Color

Creates a colour from cyan, magenta, yellow and key components.

This is the naive conversion, not a colour-managed one: it takes no account of an output profile, so it is right for generating colours and wrong for predicting what a printing press will do.

Parameters

  • c (number) — 0-100.
  • m (number) — 0-100.
  • y (number) — 0-100.
  • k (number) — 0-100.
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.xyz()

imagine.Color.xyz(x, y, z, a) -> Color

Creates a colour from CIE XYZ tristimulus values, against the D65 white point.

Parameters

  • x (number)
  • y (number)
  • z (number)
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.lab()

imagine.Color.lab(l, a_star, b_star, a) -> Color

Creates a colour from CIE Lab*, against the D65 white point.

Lab is perceptually uniform, which makes it the right space for interpolating between two colours when the intermediate steps need to look evenly spaced.

Parameters

  • l (number) — Lightness, 0-100.
  • a_star (number) — The green-red axis.
  • b_star (number) — The blue-yellow axis.
  • a (?number) — The alpha channel, 0-255 - Default: 255.

Returns Color

Color.parse()

imagine.Color.parse(value) -> Color

Turns whatever a caller passed into a Color.

Every drawing method calls this on its colour argument, so a colour can be written the shortest way that reads clearly at the call site. Accepted forms:

  • a Color, returned unchanged
  • a hexadecimal string, '#4f46e5' or '4f46e5'
  • a CSS colour name, 'rebeccapurple'
  • a packed 0xRRGGBBAA number
  • a list of 3 or 4 channels, [255, 0, 0]

Parameters

  • value (Color|string|number|list)

Returns Color

Raises ImageError When value is not a colour in any of those forms.

Color.to_packed()

imagine.Color.to_packed() -> number

Returns the colour as a packed 0xRRGGBBAA integer.

Returns number

Color.to_hex()

imagine.Color.to_hex(always_alpha) -> string

Returns the colour as a hexadecimal string with a leading #.

The alpha digits are included only when the colour is not fully opaque, unless always_alpha asks for them, so the common case reads as the familiar six digits.

%> Color(255, 0, 0).to_hex()
'#ff0000'
%> Color(255, 0, 0, 128).to_hex()
'#ff000080'

Parameters

  • always_alpha (?bool) — Always emit the alpha digits - Default: false.

Returns string

Color.to_hsl()

imagine.Color.to_hsl() -> dict

Returns the colour in HSL, as a dictionary holding h (degrees), s and l (percentage points) and a (0-255).

Returns dict

Color.to_hsv()

imagine.Color.to_hsv() -> dict

Returns the colour in HSV, as a dictionary holding h (degrees), s and v (percentage points) and a (0-255).

Returns dict

Color.to_hwb()

imagine.Color.to_hwb() -> dict

Returns the colour in HWB, as a dictionary holding h (degrees), w and b (percentage points) and a (0-255).

Returns dict

Color.to_cmyk()

imagine.Color.to_cmyk() -> dict

Returns the colour in CMYK, as a dictionary holding c, m, y and k (percentage points) and a (0-255).

Returns dict

Color.to_xyz()

imagine.Color.to_xyz() -> dict

Returns the colour in CIE XYZ, as a dictionary holding x, y, z and a (0-255).

Returns dict

Color.to_lab()

imagine.Color.to_lab() -> dict

Returns the colour in CIE Lab*, as a dictionary holding l, a_star, b_star and a (0-255).

The axes are named a_star and b_star rather than a and b so neither collides with the alpha channel.

Returns dict

Color.with_alpha()

imagine.Color.with_alpha(a) -> Color

Returns a copy of the colour with a different alpha channel.

Parameters

  • a (number) — The new alpha, 0-255.

Returns Color

Color.fade()

imagine.Color.fade(factor) -> Color

Returns a copy of the colour with its alpha scaled by a factor.

A factor of 0.5 halves whatever opacity the colour already had, rather than setting it to half opaque, so fading an already translucent colour behaves the way stacking two layers would.

Parameters

  • factor (number) — 0 (transparent) to 1 (unchanged).

Returns Color

Color.lighten()

imagine.Color.lighten(amount) -> Color

Returns a lighter version of the colour, keeping its hue.

Parameters

  • amount (number) — Percentage points of HSL lightness to add, 0-100.

Returns Color

Color.darken()

imagine.Color.darken(amount) -> Color

Returns a darker version of the colour, keeping its hue.

Parameters

  • amount (number) — Percentage points of HSL lightness to remove, 0-100.

Returns Color

Color.saturate()

imagine.Color.saturate(amount) -> Color

Returns a more saturated version of the colour.

Parameters

  • amount (number) — Percentage points of HSL saturation to add, 0-100.

Returns Color

Color.desaturate()

imagine.Color.desaturate(amount) -> Color

Returns a less saturated version of the colour.

An amount of 100 removes all colour, which is not the same as to_grayscale(): this keeps HSL lightness, that one weights the channels for perceived brightness.

Parameters

  • amount (number) — Percentage points of HSL saturation to remove, 0-100.

Returns Color

Color.rotate_hue()

imagine.Color.rotate_hue(degrees) -> Color

Returns the colour rotated around the hue wheel.

Parameters

  • degrees (number) — How far to rotate. Negative rotates backwards.

Returns Color

Color.mix()

imagine.Color.mix(other, t) -> Color

Returns a linear blend of this colour and another.

All four channels are interpolated, alpha included, so mixing a transparent colour in also makes the result more transparent.

Parameters

  • other (Color|string|number|list) — The colour to blend toward.
  • t (number) — 0 returns this colour, 1 returns other.

Returns Color

Color.over()

imagine.Color.over(backdrop) -> Color

Composites this colour over an opaque backdrop and returns the flattened result.

This is what happens when a translucent colour is drawn onto a solid background, and it is how a colour has to be resolved before writing it to a format with no alpha channel, such as JPEG.

Parameters

  • backdrop (Color|string|number|list)

Returns Color

Color.luminance()

imagine.Color.luminance() -> number

Returns the relative luminance of the colour, 0 (black) to 1 (white), as defined by WCAG 2.

The alpha channel is ignored: luminance is a property of the colour itself, and a translucent colour’s apparent brightness depends on whatever is behind it. Composite with over() first if that matters.

Returns number

Color.contrast_ratio()

imagine.Color.contrast_ratio(other) -> number

Returns the WCAG 2 contrast ratio between this colour and another, from 1 (identical) to 21 (black against white).

Text is generally held to need a ratio of at least 4.5 against its background, or 3 at large sizes.

Parameters

  • other (Color|string|number|list)

Returns number

Color.is_dark()

imagine.Color.is_dark() -> bool

Returns true when the colour is dark enough that light text reads better on it than dark text.

The threshold is the luminance at which contrast against white overtakes contrast against black.

Returns bool

Color.is_light()

imagine.Color.is_light() -> bool

The opposite of is_dark().

Returns bool

Color.best_contrast()

imagine.Color.best_contrast(light, dark) -> Color

Returns whichever of two colours reads better on top of this one.

Parameters

  • light (?Color|string) — The colour to use on a dark background - Default: white.
  • dark (?Color|string) — The colour to use on a light background - Default: black.

Returns Color

Color.to_grayscale()

imagine.Color.to_grayscale() -> Color

Returns the grey with the same perceived brightness as this colour, keeping its alpha.

This uses the Rec. 601 luma weights, which is what image editors mean by “greyscale” and what Image.grayscale() applies. It is deliberately not [[colors.grayscale]], which sets HSL saturation to zero and so keeps a bright yellow as bright as a dark blue.

Returns Color

Color.invert()

imagine.Color.invert() -> Color

Returns the colour with its red, green and blue channels inverted.

Alpha is left alone, since inverting it would turn a visible colour invisible rather than changing how it looks.

Returns Color

Color.equals()

imagine.Color.equals(other) -> bool

Returns true when both colours have identical channels.

Parameters

  • other (any)

Returns bool

Color.to_list()

imagine.Color.to_list() -> list

Returns the colour as a list of its four channels.

Returns list

Color.to_dict()

imagine.Color.to_dict() -> dict

Returns the colour as a dictionary holding r, g, b and a.

Returns dict

Color.to_string()

imagine.Color.to_string()

2026, Richard Ore and Zuri contributors