Spectra
WalkthroughThis notebook provides basic documentation of the spectra
Python library, which aims to simplify the process of creating color scales and converting colors from one "color space" to another.
In [1]:
import spectra
Note: To visually display the colors we create, let's define and use this swatches
function:
In [2]:
from IPython.display import HTML
In [3]:
swatch_template = """
<div style="float: left;">
<div style="width: 50px; height: 50px; background: {0};"></div>
<div>{0}</div>
</div>
"""
swatch_outer = """
<div style='width: 500px; overflow: auto; font-size: 10px;
font-weight: bold; text-align: center; line-height: 1.5;'>{0}</div>
"""
In [4]:
def swatches(colors):
hexes = (c.hexcode.upper() for c in colors)
html = swatch_outer.format("".join(map(swatch_template.format, hexes)))
return HTML(html)
The easiest way to create a color is to use these shortcuts, one for each "color space" spectra
supports:
spectra.rgb(r, g, b)
spectra.hsl(h, s, l)
spectra.hsv(h, s, v)
spectra.lab(l, a, b)
spectra.lch(l, c, h)
spectra.cmy(c, m, y)
spectra.cmyk(c, m, y, k)
spectra.xyz(x, y, z)
You can also pass a WC3 color name (e.g., "papayawhip"
) or hexcode (e.g., "#fefefe"
) to spectra.html(color)
, which will create the corresponding rgb
color.
For example:
In [5]:
swatches([ spectra.html("tomato") ])
Out[5]:
In [6]:
swatches([ spectra.rgb(1, 0.39, 0.28) ])
Out[6]:
Instances of spectra.Color
have four main properties:
.values
: An array representation of the color's values in its own color space, e.g. (L, a, b)
for an lab
color..hexcode
: The hex encoding of this color, e.g. #ffffff
for rgb(255, 255, 255)
/html("white")
..rgb
: The (r, g, b)
values for this color in the rgb
color space; these are allowed to go out of gamut..clamped_rgb
: The "clamped" (r, g, b)
values for this color in the rgb
color space.Note on .rgb
and .rgb_clamped
: Spectra follows colormath's convention:
RGB spaces tend to have a smaller gamut than some of the CIE color spaces. When converting to RGB, this can cause some of the coordinates to end up being out of the acceptable range (0.0-1.0 or 1-255, depending on whether your RGB color is upscaled). [...] Rather than clamp these for you, we leave them as-is.
In [7]:
tomato = spectra.lab(62.28, 57.67, 46.29)
In [8]:
tomato.values
Out[8]:
In [9]:
tomato.rgb
Out[9]:
In [10]:
tomato.clamped_rgb
Out[10]:
In [11]:
tomato.hexcode
Out[11]:
In [12]:
tomato.to("lch").values
Out[12]:
The following spectra.Color
methods return new colors:
.blend(other_color, ratio=0.5)
.brighten(amount=10)
.darken(amount=10)
.saturate(amount=10)
.desaturate(amount=10)
The parameter for .brighten/.darken is a positive/negative linear adjustment to the L
(ightness) value of the color's Lab representation. (Spectra converts the color to Lab, makes the change, and then converts back to the original color space.)
Likewise, the parameter for .saturate/.desaturate is a positive/negative linear adjustment to the c
(hroma) value of the color's Lch representation.
In [13]:
yellow = spectra.html("yellow").to("lab")
In [14]:
tomato.blend(yellow, 0.25).hexcode
Out[14]:
In [15]:
swatches([
tomato,
tomato.blend(yellow, 0.25),
tomato.blend(yellow, 0.75),
yellow
])
Out[15]:
In [16]:
swatches([
tomato.brighten(30),
tomato,
tomato.darken(30)
])
Out[16]:
In [17]:
swatches([
tomato.saturate(40),
tomato,
tomato.desaturate(40)
])
Out[17]:
In [18]:
start = spectra.html("#21313E")
end = spectra.html("#EFEE69")
swatches([ start, end ])
Out[18]:
In [19]:
scale = spectra.scale([ start, end ])
In [20]:
scale(0.5)
Out[20]:
In [21]:
scale(0.5).hexcode
Out[21]:
In [22]:
swatches([
scale(0),
scale(0.5),
scale(1)
])
Out[22]:
To set a custom domain, call .domain([ start_num, end_num ])
:
In [23]:
ten_twenty_scale = scale.domain([ 10, 20 ])
In [24]:
swatches([
ten_twenty_scale(10),
ten_twenty_scale(15),
ten_twenty_scale(20)
])
Out[24]:
The .range(count)
method produces an evenly-spaced list of colors:
In [25]:
my_range = ten_twenty_scale.range(10)
In [26]:
[ x.hexcode for x in my_range ]
Out[26]:
In [27]:
swatches(my_range)
Out[27]:
spectra.range(colors, count)
provides a shortcut to the same results:
In [28]:
swatches(spectra.range([ start, end ], 10))
Out[28]:
You can also pass plain hexcode or web-color strings to range
and scale
:
In [29]:
swatches(spectra.range([ "#21313E", "#EFEE69" ], 10))
Out[29]:
The colors produced by scales and ranges depend on the color space you're using. You can change the color space by calling .colorspace(space)
. To wit:
In [30]:
ranges_html = ""
for space in sorted(spectra.COLOR_SPACES.keys()):
converted_scale = scale.colorspace(space)
ranges_html += "<div style='margin-top: 0.5em;'>" + space + "</div>"
ranges_html += swatches(converted_scale.range(10)).data
HTML(ranges_html)
Out[30]:
(Credit to Gregor Aisch for that example.)
In [31]:
red, gray, green = [ spectra.html(x).to("lab") for x in ("red", "#CCC", "green") ]
polylinear_scale = spectra.scale([ red, gray, green ])
swatches(polylinear_scale.range(9))
Out[31]:
Note: If you want to customize a polylinear scale's domain
, the domain must be the same length as the scale itself. For example:
In [32]:
polylinear_negpos = polylinear_scale.domain([ -1, 0, 1 ])
In [33]:
swatches([
polylinear_negpos(-0.75),
polylinear_negpos(0.2),
polylinear_negpos(1)
])
Out[33]:
Much appreciated! Please open an issue on the spectra
GitHub page.