Representation

A consideration on correctness

import numpy as np
from matplotlib import pyplot as plt


def wavelength_to_rgb(wavelength_nm, gamma=0.8):
    """Convert wavelength (nm) to RGB using Dan Bruton's algorithm.

    This is the canonical algorithm used by Wikipedia and most spectrum
    visualizations. Source: http://www.physics.sfasu.edu/astro/color/spectra.html
    """
    wl = wavelength_nm

    # Piecewise linear RGB assignment
    if 380 <= wl < 440:
        r = (440 - wl) / (440 - 380)
        g = 0.0
        b = 1.0
    elif 440 <= wl < 490:
        r = 0.0
        g = (wl - 440) / (490 - 440)
        b = 1.0
    elif 490 <= wl < 510:
        r = 0.0
        g = 1.0
        b = (510 - wl) / (510 - 490)
    elif 510 <= wl < 580:
        r = (wl - 510) / (580 - 510)
        g = 1.0
        b = 0.0
    elif 580 <= wl < 645:
        r = 1.0
        g = (645 - wl) / (645 - 580)
        b = 0.0
    elif 645 <= wl <= 780:
        r = 1.0
        g = 0.0
        b = 0.0
    else:
        r = g = b = 0.0

    # Intensity falloff at edges of visible spectrum
    if 380 <= wl < 420:
        factor = 0.3 + 0.7 * (wl - 380) / (420 - 380)
    elif 420 <= wl <= 700:
        factor = 1.0
    elif 700 < wl <= 780:
        factor = 0.3 + 0.7 * (780 - wl) / (780 - 700)
    else:
        factor = 0.0

    # Apply intensity factor and gamma correction
    r = (r * factor) ** gamma
    g = (g * factor) ** gamma
    b = (b * factor) ** gamma

    return np.array([r, g, b])


def plot_visible_spectrum(wavelength_range=(380, 700), resolution=300):
    """Plot the visible spectrum with gradient overlays for lightness.

    X-axis: wavelength in nm
    Y-axis: ranges from -3 to 3
    Pure spectral colors throughout, with white gradient overlay (y=1 to 3)
    and black gradient overlay (y=-1 to -3).
    """
    wavelengths = np.linspace(wavelength_range[0], wavelength_range[1], resolution)

    # Build the base spectrum image (same color for all rows)
    image = np.zeros((resolution, resolution, 3))
    for j, wl in enumerate(wavelengths):
        rgb = wavelength_to_rgb(wl)
        image[:, j] = rgb

    fig_, ax_ = plt.subplots(figsize=(10, 6))
    extent = [wavelength_range[0], wavelength_range[1], -3, 3]

    # Plot the base spectrum
    ax_.imshow(image, aspect='auto', extent=extent)

    # White gradient overlay (y=1 to y=3, alpha 0 to 1)
    white_gradient = np.ones((resolution, resolution, 4))  # RGBA white
    for i in range(resolution):
        y = 3 - 6 * i / (resolution - 1)  # row to y mapping
        if y > 1:
            alpha = (y - 1) / 2  # 0 at y=1, 1 at y=3
        else:
            alpha = 0
        white_gradient[i, :, 3] = alpha
    ax_.imshow(white_gradient, aspect='auto', extent=extent)

    # Black gradient overlay (y=-1 to y=-3, alpha 0 to 1)
    black_gradient = np.zeros((resolution, resolution, 4))  # RGBA black
    for i in range(resolution):
        y = 3 - 6 * i / (resolution - 1)  # row to y mapping
        alpha = (-1 - y) / 2  if y < -1 else 0
        black_gradient[i, :, 3] = alpha
    ax_.imshow(black_gradient, aspect='auto', extent=extent)

    ax_.set_xlabel('Wavelength (nm)')
    # ax_.set_ylabel('y')
    ax_.set_title('Visible Spectrum')
    # ax_.axhline(y=1, color='white', linestyle='--', alpha=0.3, linewidth=0.5)
    # ax_.axhline(y=-1, color='white', linestyle='--', alpha=0.3, linewidth=0.5)
    plt.tight_layout()
    return fig_, ax_


fig, ax = plot_visible_spectrum()

ax.scatter(589, -1, s=100, edgecolor="k", facecolor="none", linewidth=2)

https://xkcd.com/1080/

blind spot interactive