Packages

colors

Parse CSS colors, convert between RGB, hex and HSL, and lighten, darken and mix them.

import "std:colors"

std:colors reads colors in many shapes (#ff8800, rgb(255, 136, 0), tomato, hsl(32, 100%, 50%)), changes them the way people think about them (lighter, darker, a blend of two) and writes them back out as hex text.

A color here is one of three things, and the functions accept any of them where they take a color:

  • a CSS color string such as "#336699" or "tomato",
  • an object with r, g, b and a from 0 to 255, which is what colors.parse returns,
  • an img.Color made with img.rgb.

Parsing#

colors.parse(text) returns a result holding {r, g, b, a}:

main.osl
import "std:colors"

auto orange = colors.parse("#ff8800").unwrap()
log orange
log orange.r orange.g orange.b orange.a
Click the code to edit it

Alpha a is opacity: 255 is solid. It reads the common CSS forms:

main.osl
import "std:colors"

string[] inputs = [
  "#f80", "#ff8800", "#ff880080", "rgb(10, 20, 30)", "hsl(120, 100%, 50%)", "red"
]
for text in inputs (
  auto c = colors.parse(text).unwrap()
  log text colors.stringify(c)
)
Click the code to edit it

Text that is not a color is a ParseError:

main.osl
import "std:colors"

auto bad = colors.parse("not a color")
log bad.isErr()
log bad.unwrapErr().kind
Click the code to edit it

Writing colors out#

colors.stringify gives #rrggbb, or #rrggbbaa when the color isn't fully opaque. It accepts any of the color forms:

main.osl
import "std:colors"

log colors.stringify({r: 255, g: 99, b: 71, a: 255})
log colors.stringify({r: 1, g: 2, b: 3, a: 128})
log colors.stringify("tomato")
Click the code to edit it

Lighter, darker, mixed#

lighten and darken move the lightness by an amount from 0 to 1. mix(a, b, amount) blends two colors, where 0 is all a and 1 is all b. All three return hex text:

main.osl
import "std:colors"

string base = "#336699"
log colors.lighten(base, 0.2)
log colors.darken(base, 0.2)
log colors.mix("#ff0000", "#0000ff", 0.5)
log colors.mix("black", "white", 0.25)
Click the code to edit it

These work on lightness in HSL, which is how people judge "lighter", rather than adding to the RGB channels.

HSL#

Hue, saturation and lightness make some changes easy that are awkward in RGB, such as rotating the hue to find a related color. toHSL gives {h, s, l, a} with hue in degrees (0 to 360) and the others from 0 to 1. fromHSL goes back:

main.osl
import "std:colors"

auto hsl = colors.toHSL("#ff8800")
log hsl
log hsl.h

auto shifted = colors.fromHSL({h: hsl.h + 120, s: hsl.s, l: hsl.l})
log shifted
Click the code to edit it

Hue 152 is a green, 120 degrees around the wheel from orange.

Example: a palette from one color#

Starting from a brand color, build a scale for a UI: three tints, the base, and two shades.

main.osl
import "std:colors"

string brand = "#336699"
string[] steps = []
for amount in [0.3, 0.15] (
  steps.append(colors.lighten(brand, amount))
)
steps.append(brand)
for amount in [0.1, 0.2] (
  steps.append(colors.darken(brand, amount))
)
log steps
Click the code to edit it

Colors in images#

The three color shapes convert freely, so img.rgb(255, 0, 0) can go into colors.lighten, and a hex string or the object from colors.parse can go into image.fill. Use whichever is handy and let colors.stringify normalise to text.

Quick check

colors.parse("not a color") is called. What happens?

Your turn

The opposite color

Finish complement so it returns the color opposite on the wheel: the same saturation and lightness with the hue turned 180 degrees (wrap past 360 with % 360).

main.osl
Ctrl Enter runs
Expected output
#0077ff
#00ffff

API reference#

12 members, generated from the std:colors source of OSL 9.3.0.

Functions

#colors.darken(any color, number amount) string

darken lowers color's HSL lightness by amount (0 to 1, where 1 is the whole range, so darken(c, 1) is black). Returns "#rrggbb" or "#rrggbbaa". Throws ValueError for an invalid color or amount.

#colors.fromHSL(object hsl) string

fromHSL converts {h, s, l, a} (as toHSL returns it; a is optional and defaults to 255) to a "#rrggbb" or "#rrggbbaa" color. The hue wraps around 360 degrees. Throws ValueError for a missing key, saturation or lightness outside 0 to 1, or alpha outside 0-255.

#colors.gray(any v) object

Deprecated. Deprecated: use {r: v, g: v, b: v} instead.

gray builds a grayscale {Y} from a channel that wraps modulo 256.

#colors.hex(any hex) object

Deprecated. Deprecated: use colors.parse(text) instead, which reports invalid input.

hex parses "#rrggbb" or "#rrggbbaa" into {R, G, B, A}, giving opaque black for anything else.

#colors.lighten(any color, number amount) string

lighten raises color's HSL lightness by amount (0 to 1, where 1 is the whole range, so lighten(c, 1) is white). Returns "#rrggbb" or "#rrggbbaa". Throws ValueError for an invalid color or amount.

#colors.mix(any a, any b, number amount) string

mix blends two colors channel by channel, alpha included: amount 0 gives a, 1 gives b and 0.5 the midpoint. Returns "#rrggbb" or "#rrggbbaa". Throws ValueError for an invalid color or an amount outside 0 to 1.

#colors.nrgba(any r, any g, any b, any a) object

Deprecated. Deprecated: use {r: r, g: g, b: b, a: a} instead.

nrgba builds {R, G, B, A} from four channels, which wrap modulo 256.

#colors.parse(string text) result<{a: int, b: int, g: int, r: int}, error>

parse reads a CSS color and returns it as {r, g, b, a} with 0-255 channels, which every OSL color argument accepts. It understands "#rgb", "#rgba", "#rrggbb", "#rrggbbaa", the CSS color names (such as "red" and "rebeccapurple"), "transparent", rgb()/rgba() and hsl()/hsla() with commas or spaces, percentages and an optional "/ alpha", in any case. Fails with ParseError for anything else.

#colors.rgb(any r, any g, any b) object

Deprecated. Deprecated: use {r: r, g: g, b: b} or colors.stringify instead.

rgb builds an opaque {R, G, B, A} from three channels, which wrap modulo 256.

#colors.rgba(any r, any g, any b, any a) object

Deprecated. Deprecated: use {r: r, g: g, b: b, a: a} or colors.stringify instead.

rgba builds {R, G, B, A} from four channels, which wrap modulo 256.

#colors.stringify(any color) string

stringify returns color as "#rrggbb", or "#rrggbbaa" when it isn't fully opaque. color is a CSS color string or {r, g, b, a}. Throws ValueError for an invalid color.

#colors.toHSL(any color) {a: int, h: number, l: number, s: number}

toHSL converts color to {h, s, l, a}: hue in degrees from 0 to 360, saturation and lightness from 0 to 1, and alpha 0-255. Throws ValueError for an invalid color.