Skip to main content

A melanopic axis for colormaps: rate, rank, and generate colormaps by circadian (melanopic) content.

Project description

CI codecov

Melanopy: A melanopic axis for colormaps

Scientific colormaps are usually judged on two axes: perceptual uniformity and colour-vision-deficiency (CVD) safety. Melanopy adds a third: how much short-wavelength, melanopic (melatonin-suppressing) light a map emits. Unlike the other two, this third axis is a design dimension you choose by context, not a pass/fail gate — a sleep lab wants its protective end, a daytime alerting display the other. Melanopy makes the axis measurable, scores existing colormaps on it, and provides a one-parameter family that walks it while holding uniformity and CVD-safety fixed.

For people who read screens as their main light source at night (e.g., sleep labs, observatories, NICU, night radiology, control rooms) large data-fills (e.g., spectrograms, density maps) actually emit light, and their colour content can cooperate with, or fight, a chosen circadian lighting strategy.

This rates a colour's chromaticity, not light dose. Real circadian load also depends on screen brightness, screen fill, viewing distance, and ambient light. If you genuinely need to stay alert, the dominant lever is room lighting. The value here is measurability, a scored index, a uniformity-preserving generator, and surfacing the axis. The physiological effect of a colormap alone is second-order.

📖 Documentation: https://remrama.github.io/melanopy

Quickstart

import matplotlib.pyplot as plt
import numpy as np

import melanopy as mp

# Score any colormap on the melanopic axis  (display white = 1.0)
c = plt.get_cmap("viridis")(np.linspace(0, 1, 256))[:, :3]
print(mp.rate_colormap(c))
# {'melanopic_ratio': ~0.83, 'mp_spread': ..., 'range': (...)}

# Use the named endpoints (registered with matplotlib) on any 2-D field Z
mp.register()
Z = np.add.outer(np.linspace(0, 1, 200), np.linspace(0, 1, 200))
plt.imshow(Z, cmap="sodium")    # protective: warm, low-melanopic
plt.imshow(Z, cmap="xenon")     # alerting:   cool, high-melanopic
plt.imshow(Z, cmap="equilux")   # circadian-neutral (M/P ~ 1)

# Dial the whole axis: alpha 0 (protective) .. 1 (alerting)
cmap = mp.circadia(0.3, as_cmap=True)

# Or walk the whole axis in one map, or diverge for signed data
seq = mp.circadia_sweep()        # protective -> alerting; melanopic ratio ~linear in the data
div = mp.circadia_diverging()    # signed: warm protective <- neutral -> cool alerting

melanopic_ratio < 1 → protective (warm); > 1 → alerting (cool/blue).

The two metrics

  • M/P mean (melanopic_ratio) — where a map sits on the axis (white = 1).
  • M/P spread (σ) (mp_spread) — how tightly it sits. A map can be mildly protective on average yet smeared (e.g. viridis dumps blue at its dark end); a tight spread reads as a "pure" ramp. Both numbers are luminance-weighted, so near-black pixels that emit almost nothing don't dominate either one.

What scoring the existing maps reveals

Applying the rater to the colormaps people already use settles how much new design is even needed — three non-obvious facts fall out (full table in index/):

  • A protective, pure map already existscopper (M/P 0.49, σ 0.03) and sodium (0.29); the protective end just needed naming, not inventing.
  • The popular uniform maps are smeared — viridis / magma / inferno / cividis / plasma sit mid-axis but dump high-melanopic blue at their dark, low-data end (σ ≈ 0.4–1.0).
  • The genuine gap is a uniform, CVD-safe alerting map — the slot the Circadia family's xenon endpoint is built to fill.

Scope & novelty

  • Borrowed — the melanopic metrology: the CIE S 026 melanopic action spectrum + V(λ), validated against the luox reference calculator (melanopy reproduces the CIE S 026 D65 constant to five significant figures — see manuscript/luox_crosscheck.md).
  • New — the port to colormaps: the per-display three-coefficient collapse (any sRGB colour → a weighted sum of three per-primary M/P numbers, so no runtime spectral integration); the mean + spread decomposition that catches "smeared" maps; and the constraint-preserving generator in which melanopic content is emergent, never optimized.

Display panels

Melanopic content depends on the display's primary spectra, which sRGB doesn't fix — so the rater takes a panel= argument selecting among representative archetypes (representative narrowband, led_lcd blue-pump white-LED, oled, wide_gamut quantum-dot). Absolute M/P shifts with the panel (the blue coefficient ranges ≈8.8 to ≈13.7), but the ranking is robust: Spearman ρ ≥ 0.99 across panels, and display white stays exactly 1.0 (see index/). For exact numbers on a specific monitor, plug its measured primary SPDs into melanopy.spectra.coefficients_from_primaries.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

melanopy-0.0.1.tar.gz (41.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

melanopy-0.0.1-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

Details for the file melanopy-0.0.1.tar.gz.

File metadata

  • Download URL: melanopy-0.0.1.tar.gz
  • Upload date:
  • Size: 41.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for melanopy-0.0.1.tar.gz
Algorithm Hash digest
SHA256 e72782cd3afa4a4c1b7c27e7e0e4a4f7852fcf1e82d75fa32f9971a55c16a850
MD5 335636be960b6b5bde79720df349f765
BLAKE2b-256 e02ba13eb87cadb595ee6d070bfbc6168f663ad3549d140ea744cc57c909e4a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for melanopy-0.0.1.tar.gz:

Publisher: release.yml on remrama/melanopy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file melanopy-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: melanopy-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 35.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for melanopy-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d4207d7dd4a5cf82b2d2814756e20852daf7838f68efb17bec967d256bb70ad6
MD5 3719b1fd9494f63bfb5ddcab18587269
BLAKE2b-256 ca6a9ace3348bb78cf84b910987e6bc0bb5864187cb4b769464a7692bd21a569

See more details on using hashes here.

Provenance

The following attestation bundles were made for melanopy-0.0.1-py3-none-any.whl:

Publisher: release.yml on remrama/melanopy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page