09 / 14 · certificates
Palette Ceiling
A certified upper bound on how separable any N-color palette can be, worst case across CVD, bracketed by a witness palette.
The problem
Published figures for how many distinguishable colors a display can show are napkin math: sphere-packing constants applied to the volume of a color solid, for one observer under one light. They answer nothing about your case. Given twelve classes and a requirement to survive protan and deutan vision, what is the best minimum pairwise separation any palette could possibly achieve? Without that ceiling you cannot tell whether a disappointing palette is a bad solve or an impossible ask.
The method
The tool samples the sRGB cube on a deterministic lattice, gamut_steps per axis, and views every point through normal vision and each declared CVD profile (Machado 2009), measuring separation in the lab's worst-case metric: the minimum ΔE_OK across all views. The certificate is a covering argument. If K balls of radius r cover the sampled gamut and K is less than N, any N colors must place two in one ball, so no N-palette can exceed a minimum pairwise separation of 2r; binary search on r finds the tightest ceiling the sample supports. A maximin solver then constructs a witness palette as the floor, so the true optimum is bracketed inside [floor, ceiling] with the gap reported, and an optional threshold walks N upward to report the largest class count the solver could still satisfy.
What it promises
- The ceiling is computed, not estimated: a covering argument over the sampled gamut in the worst-case metric across normal vision and every declared CVD profile, with the sample density reported alongside it.
- The floor is constructive: a real witness palette achieving achieved_floor_delta_e_ok exists, so the truth lies inside [floor, ceiling] and the gap is stated rather than split.
- Given a threshold, feasible_n_at_threshold reports the largest class count at which the solver still met it, a demonstrated palette, not an extrapolation.
Calling it
{
"tool": "certify_palette_ceiling",
"arguments": {
"n_classes": 12,
"cvd_profiles": [
{
"type": "protan",
"severity": 1
},
{
"type": "deutan",
"severity": 1
}
],
"threshold_delta_e_ok": 0.06
}
} Clone the maryslab repo, run cd mcp && npm install && npm run build, then claude mcp add maryslab -- node mcp/dist/server.js
A worked example
What is the best separation any 6-colour palette could achieve, and how close does a real one get?
what was asked
{
"n_classes": 6,
"cvd_profiles": [],
"gamut_steps": 7
} what the engine answered
{
"n_classes": 6,
"certified_ceiling_delta_e_ok": 0.6714805746078493,
"achieved_floor_delta_e_ok": 0.2963856329826682,
"gap": 0.3750949416251811,
"feasible_n_at_threshold": null,
"gamut_sample": {
"steps": 7,
"points": 343
},
"prior_art_note": "Published \"how many distinguishable colors\" figures are sphere-packing estimates for one observer under one light. This computes the bound for YOUR class count under the worst case across the declared CVD profiles, and pairs it with a constructed witness palette, so the true optimum is bracketed.",
"note": "Ceiling from a covering argument over a finite gamut sample: if K(r) balls cover the gamut and K < N, two of any N colors share a ball, so min separation <= 2r. A denser gamutSteps tightens the ceiling; the floor is a constructed witness palette, so the truth lies inside [floor, ceiling].",
"meta": {
"engine_version": "0.4.1",
"engine_backend": "reference-ts",
"dataset_snapshot": "usgs-splib07a-1",
"compute_device": "cpu"
}
} how to read it
The truth sits between achieved_floor (a palette that exists) and certified_ceiling (a bound from a covering argument). A wide gap is honest uncertainty, not a result.
Produced by running certify_palette_ceiling at build time (27 ms). Long arrays are truncated for reading; nothing else is edited.
Known limits
- The ceiling depends on gamut sample density: the cover is built on a finite lattice, 2r is exact for the sampled points, and off-lattice colors are covered only up to the lattice's slack, so gamut_steps is part of the claim and a denser grid tightens it.
- The floor is whatever the seeded maximin solver found under your budget, a constructed palette rather than a proven optimum; a wide gap is honest uncertainty about where the true optimum sits.
- feasible_n_at_threshold stops at the first class count the solver fails, so it certifies feasibility up to that N, not impossibility beyond it.