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.