Skip to content

API reference

The public API is curated in melanopy.__all__. Everything below is importable directly from the top-level package, e.g. import melanopy as mp; mp.rate_colormap(...).

Rating

rate_colormap

rate_colormap(colors, panel='representative', profile=False)

Rate an sRGB ramp on the melanopic axis (display white = 1.0).

Both summary numbers are luminance-weighted and ignore near-black pixels (which emit almost nothing), so neither is dominated by the dark end of the ramp.

Parameters:

Name Type Description Default
colors array - like

An (N, 3) sRGB ramp, values in [0, 1].

required
panel str

Display archetype selecting the per-primary coefficients (see :data:coeffs.PANELS).

'representative'
profile bool

When True, also return the per-position arrays behind the summary numbers.

False

Returns:

Type Description
dict

melanopic_ratio — the M/P mean (luminance-weighted axis position; < 1 protective,

1 alerting); mp_spread — the luminance-weighted spread of the per-position ratio (a tight spread reads as a "pure" ramp); and range — its (min, max) over the emitting ramp. With profile=True the dict also carries positions (the [0, 1] data grid), ratios (per-position M/P, NaN where the colour emits ~nothing), and luminance (the per-position photopic luminance, i.e. the weights) — enough to plot the M/P profile.

Examples:

>>> import matplotlib.pyplot as plt, numpy as np, melanopy as mp
>>> c = plt.get_cmap("viridis")(np.linspace(0, 1, 256))[:, :3]
>>> mp.rate_colormap(c)
{'melanopic_ratio': 0.834, 'mp_spread': 0.556, 'range': (0.395, 3.069)}

melanopic_ratio

melanopic_ratio(rgb, panel='representative')

Melanopic ratio (M/P) of one or more sRGB colours, normalized so display white = 1.0.

< 1 is protective (warm, low melatonin drive); > 1 is alerting (cool, blue-rich).

Parameters:

Name Type Description Default
rgb array - like

A single (3,) sRGB colour or an (N, 3) array, values in [0, 1].

required
panel str

Display archetype selecting the per-primary coefficients (see :data:coeffs.PANELS).

'representative'

Returns:

Type Description
ndarray

The melanopic ratio per input colour.

Examples:

>>> import melanopy as mp
>>> mp.melanopic_ratio([1, 1, 1])  # display white is the unit
array([1.])
>>> mp.melanopic_ratio([1, 0, 0])  # pure red — protective
array([0.002])
>>> mp.melanopic_ratio([0, 0, 1])  # pure blue — strongly alerting
array([8.695])

circadia_rating

circadia_rating(alpha, *, panel='representative')

Rated melanopic ratio of a Circadia map at alpha — the physical number behind the dial.

alpha is a geometric position on the OKLab morph, not a melanopic ratio, and the M/P a viewer actually receives is panel-dependent. This composes the generator and the rater so a live UI (e.g. a labelled slider) can show the rated M/P for its configured panel rather than bare alpha. The recompute is cheap (a vectorized 256-point rating); a hot UI may memoize on (round(alpha, 3), panel).

Parameters:

Name Type Description Default
alpha float

Position on the Circadia axis in [0, 1] (0 protective/warm, 1 alerting/cool).

required
panel str

Display archetype selecting the per-primary coefficients (see :data:coeffs.PANELS).

'representative'

Returns:

Type Description
tuple of float

(melanopic_ratio, mp_spread) — the M/P mean (axis position; < 1 protective, > 1 alerting) and its luminance-weighted spread, both for panel.

Examples:

>>> import melanopy as mp
>>> mp.circadia_rating(0.0)  # Sodium endpoint — protective
(0.288, 0.067)
>>> mp.circadia_rating(0.55)  # Equilux — circadian-neutral (M/P ≈ 1)
(0.999, 0.16)
>>> mp.circadia_rating(1.0)  # Xenon endpoint — alerting
(1.728, 0.423)

Generator — the Circadia family

circadia

circadia(alpha, n=256, as_cmap=False, name=None)

One member of the Circadia family — a perceptually-uniform, CVD-safe ramp at dial alpha.

alpha sets the warm→cool colour temperature: 0 is protective (Sodium, warm, low melanopic), 1 is alerting (Xenon, cool, high melanopic), and ~0.55 is the M/P ≈ 1 crossover. It is the design input; read the emergent melanopic ratio back with :func:circadia_rating or :func:rate_colormap.

Parameters:

Name Type Description Default
alpha float

Position on the Circadia axis in [0, 1].

required
n int

Number of samples in the ramp.

256
as_cmap bool

Return a :class:matplotlib.colors.ListedColormap instead of the raw array.

False
name str

Name for the colormap when as_cmap=True (default "circadia_<alpha>").

None

Returns:

Type Description
ndarray or ListedColormap

An (n, 3) sRGB array, or a colormap when as_cmap=True.

Examples:

>>> import melanopy as mp
>>> mp.circadia(0.3).shape
(256, 3)
>>> cmap = mp.circadia(0.3, as_cmap=True)  # drop-in matplotlib colormap
>>> cmap.name
'circadia_0.30'

circadian_cmap is an alias of circadia.

circadia_sweep

circadia_sweep(n=256, as_cmap=False, name=None)

Full-axis sequential map sweeping protective (Sodium, warm) -> alerting (Xenon, cool), with the melanopic ratio rising ~linearly along the ramp -- the "data axis is the melanopic axis" teaching map. The shared monotone OKLab lightness keeps it ordered and CVD-recoverable; the positions are calibrated against the rater so M/P is linear in the data value. This is the one melanopic-aware generator (via a local import); circadia itself stays pure OKLab geometry.

Examples:

>>> import melanopy as mp
>>> mp.circadia_sweep().shape
(256, 3)
>>> mp.circadia_sweep(as_cmap=True).name
'circadia_sweep'

circadia_diverging

circadia_diverging(n=256, as_cmap=False, name=None)

Diverging map for signed data: warm Sodium (protective, M/P<1) <- light neutral centre (M/P=1) -> cool Xenon (alerting, M/P>1). Each arm has monotone lightness, but -- like most diverging maps -- the arms are told apart across zero by hue, so it is NOT CVD-order-recoverable; use the sequential maps where CVD-safety matters.

Examples:

>>> import melanopy as mp
>>> mp.circadia_diverging(as_cmap=True).name  # pass to imshow(..., vmin=-x, vmax=x)
'circadia_diverging'

The named anchors SODIUM (alpha=0.0), EQUILUX (alpha=0.55, the M/P = 1 crossover), and XENON (alpha=1.0) are exported as ready-made matplotlib.colors.ListedColormap objects.

matplotlib integration

register

register()

Register sodium/xenon/equilux + circadia_sweep/circadia_diverging as matplotlib colormaps.

After calling this, the maps are available by name to any matplotlib call. Safe to call more than once.

Examples:

>>> import matplotlib.pyplot as plt, numpy as np, melanopy as mp
>>> mp.register()
>>> Z = np.add.outer(np.linspace(0, 1, 200), np.linspace(0, 1, 200))
>>> plt.imshow(Z, cmap="sodium")

Categorical palette

The CVD-safe categorical palette is exposed as CATEGORICAL (a ListedColormap), with the raw hex lists CATEGORICAL_DARK / CATEGORICAL_LIGHT and CATEGORICAL_NAMES. One palette serves every circadian regime: small categorical marks emit negligible light.

>>> import melanopy as mp
>>> mp.CATEGORICAL_NAMES
['amber', 'sky', 'teal', 'yellow', 'blue', 'vermillion', 'rose']
>>> mp.CATEGORICAL_DARK[:3]
['#F2A036', '#81CAF0', '#009C89']

Adding a measured panel

The built-in panels (representative, led_lcd, oled, wide_gamut) are spectral archetypes, not measurements — so a reported M/P is indicative, not metrological. The protective ↔ alerting ranking is stable across panels (Spearman ρ ≥ 0.99), so relative comparisons hold on any panel; only the absolute M/P needs a measured one.

For research-grade work, measure the three primary SPDs (R, G, B at full drive) on the package's 1 nm grid (melanopy.spectra.WL, 380–780 nm) and derive the coefficients:

>>> from melanopy.spectra import coefficients_from_primaries
>>> coeffs = coefficients_from_primaries({"R": spd_r, "G": spd_g, "B": spd_b})  # doctest: +SKIP

Add the result to melanopy.coeffs.PANELS as a new keyed row (e.g. "lab_monitor": {...}); the panel="lab_monitor" argument then threads through rate_colormap, melanopic_ratio, and circadia_rating.

coefficients_from_primaries

coefficients_from_primaries(primaries, smel=None, vlam=None)

Derive the three per-primary M/P coefficients from measured primary SPDs.

Parameters:

Name Type Description Default
primaries dict

{'R', 'G', 'B'} -> SPD array on :data:WL (the 380–780 nm, 1 nm grid).

required
smel array - like

Override the melanopic action spectrum / V(λ); default to the shipped CIE tables.

None
vlam array - like

Override the melanopic action spectrum / V(λ); default to the shipped CIE tables.

None

Returns:

Type Description
dict

{'R', 'G', 'B'} -> the melanopic/photopic ratio of each primary.

Examples:

>>> from melanopy.spectra import coefficients_from_primaries, panel_primaries
>>> coefficients_from_primaries(panel_primaries("representative"))
{'R': 0.003, 'G': 0.656, 'B': 10.968}