Categories: CSSScriptWeb Design

Realistic Color Mixing Library Using Kubelka-Munk Theory – Spectral.js

Spectral.js is a lightweight JavaScript library that simulates realistic color mixing based on the Kubelka-Munk theory.

It models how physical pigments interact with light, rather than relying on standard RGB or HSL color blending. This produces color combinations that match what you’d see when mixing actual paint.

You work with familiar hex, RGB, and CSS strings, while the library handles the complex spectral math behind the scenes.

Features:

  • Physics-based color mixing: Models pigment interaction using Kubelka-Munk absorption and scattering coefficients.
  • Spectral reflectance simulation: Generates reflectance curves from RGB values across the visible spectrum (380-750 nm in 10 nm steps).
  • Perceptually uniform color spaces: Uses OKLab and OKLCh for accurate gamut mapping and perceptual color comparisons.
  • Tinting strength control: Adjusts the dominance of individual colors in a mixture to prevent overly saturated results.
  • Lazy memoization: Computes color space transformations and spectral data only once, caching values for performance.
  • GLSL shader support: Includes spectral.glsl for GPU-based real-time color mixing in WebGL projects.
  • 64-bit precision: All spectral and colorimetric calculations use double-precision floating point math.

Use Cases:

  • Digital Painting Applications: Developers can create brushes that blend colors naturally like oil or acrylics.
  • Generative Art: Artists can generate palettes that maintain vibrancy. Standard RGB math often desaturates colors during transitions.
  • Data Visualization: It generates gradients that are perceptually uniform. This makes heatmaps and charts easier to read.
  • Print Simulation: The library approximates how physical inks will layer and mix before going to print.

How To Use It:

1. Install Spectral.js via npm or include the script directly in your HTML:

# NPM
$ npm install spectral.js
<script src="spectral.js"></script>

2. Basic color creation. The Color object automatically computes spectral reflectance, XYZ values, OKLab coordinates, and Kubelka-Munk parameters. These calculations are cached internally for performance.

// Create color from hex string
let blue = new spectral.Color('#002185');

// Create color from RGB string
let yellow = new spectral.Color('rgb(252, 210, 0)');

// Create color from RGB array
let red = new spectral.Color([252, 48, 48]);

3. Mixing Colors. The mix() function combines any number of colors using their spectral properties. Each color requires a mixing factor.

let color1 = new spectral.Color('#002185'); // Blue
let color2 = new spectral.Color('#FCD200'); // Yellow

// Mix equal parts blue and yellow
let green = spectral.mix([color1, 0.5], [color2, 0.5]);

console.log(green.toString()); // Outputs: #3D933E

4. The mixing factors are normalized automatically. You can use whole numbers if preferred:

let color1 = new spectral.Color('#FCF046'); // Yellow
let color2 = new spectral.Color('#E53166'); // Magenta
let color3 = new spectral.Color('#3375DA'); // Blue

// Mix using whole number ratios (1:1:1)
let brown = spectral.mix([color1, 1], [color2, 1], [color3, 1]);

console.log(brown.toString()); // Outputs: #8D7964

5. Generating Color Palettes. The palette() function creates smooth transitions between two colors using spectral interpolation.

let start = new spectral.Color('#BB0657'); // Magenta
let end = new spectral.Color('#E0E0B3'); // Beige

// Generate 8-step gradient
let colors = spectral.palette(start, end, 8);

// Convert to hex strings
let hexColors = colors.map(color => color.toString());
console.log(hexColors);
// ['#BB0657', '#C00C5C', '#CD1E6F', '#DD3889', '#E85AA0', '#ED85AF', '#E9B9B4', '#E0E0B3']

6. Create Gradients using the gradient() function.

let teal = new spectral.Color('#005E72');
let cream = new spectral.Color('#EAD9A7');
let mauve = new spectral.Color('#894B54');

// Get color at 75% position
let t = 0.75;
let result = spectral.gradient(t, [teal, 0], [cream, 0.5], [mauve, 1]);

console.log(result.toString()); // Outputs: #C7938C

7. Some colors dominate mixtures more than others. The tintingStrength property can help you control this behavior.

let red = new spectral.Color('#ff0000');
let yellow = new spectral.Color('#ffff00');

// Default mixing - red dominates
let orange1 = spectral.mix([red, 0.5], [yellow, 0.5]);
console.log(orange1.toString()); // Outputs: #FF440F (very red-orange)

// Reduce red's tinting strength to balance the mixture:
let red = new spectral.Color('#ff0000');
let yellow = new spectral.Color('#ffff00');

// Reduce red's dominance
red.tintingStrength = 0.35;

let orange2 = spectral.mix([red, 0.5], [yellow, 0.5]);
console.log(orange2.toString()); // Outputs: #FF8427 (balanced orange)

// Lower tinting strength values (below 1.0) reduce a color's influence. 
// Higher values (above 1.0) increase it.

8. The spectral.glsl file provides GPU-based color mixing for WebGL projects. It supports mixing 2 to 4 colors with optional tinting strength parameters.

// Define colors as RGB vectors
vec3 yellow = vec3(0.9734, 0.8714, 0.0612);
vec3 red = vec3(0.7835, 0.0307, 0.1329);
vec3 blue = vec3(0.0331, 0.1779, 0.7011);

// Mix colors with factors
vec3 color = spectral_mix(
  yellow, 1.0, 1.0 - p.x,    // Yellow with decreasing influence
  red, 0.5, p.x - p.y,       // Red with custom tinting strength
  blue, 1.0, p.y             // Blue with increasing influence
);

9. Available read-only properties for color data:

  • color.R (Array): Spectral reflectance curve from 380 to 750 nm in 10 nm steps (38 values).
  • color.sRGB (Array): Standard RGB color space values [r, g, b] where each component is 0-1.
  • color.lRGB (Array): Linear RGB values before gamma correction.
  • color.XYZ (Array): CIE XYZ color space representation using D65 illuminant.
  • color.OKLab (Array): OKLab perceptual color space coordinates [L, a, b].
  • color.OKLCh (Array): OKLCh cylindrical representation [L, C, h] where h is hue angle.
  • color.KS (Array): Kubelka-Munk absorption/scattering parameters across the spectrum.
  • color.luminance (Number): Perceived brightness (Y value from CIE XYZ).
  • color.tintingStrength (Number): Mixing intensity of this pigment (default: 1.0).

10. API methods.

// Check if color is within displayable sRGB gamut
let inGamut = color.inGamut({ epsilon: 0.00001 });

// Bring out-of-gamut color into displayable range
// Uses chroma reduction in OKLCh space by default
let mapped = color.toGamut({ method: 'map' });

// Alternative: clip RGB values to 0-1 range (faster but less accurate)
let clipped = color.toGamut({ method: 'clip' });

// Convert color to string representation
// Automatically applies gamut mapping if needed
let hexString = color.toString(); // Default: hex format with 'map' method
let rgbString = color.toString({ format: 'rgb' }); // RGB format
let clippedHex = color.toString({ format: 'hex', method: 'clip' }); // Clipping method

FAQs:

Q: Why do my mixed colors look different from standard RGB blending?
A: Spectral.js simulates how physical pigments absorb and scatter light. RGB blending treats color as additive light. When you mix complementary colors (like blue and yellow) in RGB, you get gray. Spectral mixing produces green because it models pigment behavior.

Q: How do I prevent one color from dominating the mixture?
A: Adjust the tintingStrength property on the dominant color. Set it below 1.0 to reduce its influence. Red often dominates yellow in 50/50 mixtures. Setting red.tintingStrength = 0.35 balances the result.

Q: What happens when a mixed color is outside the sRGB gamut?
A: The toString() method automatically applies gamut mapping using OKLCh chroma reduction by default. This brings the color into displayable range while preserving perceived hue and lightness. You can use method: 'clip' for faster processing but less accurate results. Call inGamut() to check if a color needs adjustment.

The post Realistic Color Mixing Library Using Kubelka-Munk Theory – Spectral.js appeared first on CSS Script.

rssfeeds-admin

Share
Published by
rssfeeds-admin

Recent Posts

For Earth Day, Get a Bonus $10 Gift Card on Top of Cash When Trading In Electronics at PayMore

With the weather finally getting nicer, you’re probably doing a bit of spring cleaning. In…

48 minutes ago

The Pitt Season 2 Finale: “9:00 PM” Review

Warning: This review contains full spoilers for The Pitt Season 2, Episode 15!And so we…

48 minutes ago

Disney Debuts New Avengers: Doomsday Trailer at CinemaCon 2026

MCU fans lucky enough to attend Disney's CinemaCon 2026 presentation were rewarded, as Kevin Feige…

2 hours ago

“Purdue 1” Space Mission

WEST LAFAYETTE, Ind. (WOWO) — Purdue University has its five-member all-Boilermaker crew for the “Purdue…

3 hours ago

Showers Return Saturday

FORT WAYNE, Ind. (WOWO) — Rain moves across parts of Indiana today, followed by a…

3 hours ago

The FlashForge AD5X Is One of the Best Multi Material and Multi Color 3D Printers Priced Under $300

One of the better regarded 3D printers with multi material print capability is now priced…

3 hours ago

This website uses cookies.