Region Color Is Structural in Meowdoku, Not Decoration
Every cell on a Meowdoku board is a single flat fill color, and nothing else marks where one region ends and the next begins. That makes the palette a working part of the puzzle rather than a coat of paint: pick two touching regions' colors badly and the board becomes unreadable before a single cat is placed. The game already treats it that way — what it has not checked is whether that same palette survives a color-vision deficiency, so this piece does.
What actually marks a region
Look for what draws a boundary between two regions in the board component, and there is nothing to find. Every cell gets the same 1px hairline border, whether the cell next to it belongs to its own region or a completely different one. The only thing that changes from cell to cell is the fill color: no border, no icon, no texture.
That is a real design choice, not an oversight — a bold seam at every region edge would be one more thing drawn on a board that is already twelve colors and up to a hundred cells. But it means color carries information that has nowhere else to live. "Which region is this cell in" is the first fact every rule in the game depends on, and fill color is the only place that fact is written down.
Why that makes the palette load-bearing
Once color is the sole boundary signal, two touching regions on similar colors stop reading as two regions — they read as one blob, and every deduction that depends on telling them apart becomes a squint instead of a glance.
The module that assigns those colors says as much in its own comments: a naive assignment "often produce[s] clashes like red next to rose or orange next to yellow — the player has to stare to tell whether it's one block, and 'which region is this' is the premise of every reasoning step in this game." That line is the entire justification for what follows.
The algorithm: real graph coloring, not a shuffled list
`assignRegionColors` first builds a region-adjacency graph: two regions are adjacent if any cells share an edge, deliberately using 4-neighbor contact rather than 8 — a diagonal touch is not visually misread as one block, so it is not a coloring constraint. It then processes regions by neighbor count, and for each picks the empty palette slot farthest, on the palette's own color ring, from every already-colored neighbor, breaking ties toward whatever spreads the twelve hues out furthest overall.
That is graph coloring by name and by function: regions are vertices, shared edges are constraints, palette slots stand in for colors. The one relaxation from the classic formulation is the ring itself — slots close together (red and orange) count as more similar than slots on opposite sides (red and teal), matching how those hues actually look side by side.
How much headroom the palette actually has
The palette has twelve slots. A board never uses all of them: region count equals board size, and board size tops out at ten.
| Board | Regions (= board size) | Spare palette slots |
|---|---|---|
| Easy 5×5 | 5 | 7 |
| Normal 7×7 | 7 | 5 |
| Hard 9×9 | 9 | 3 |
| Extreme 10×10 | 10 | 2 |
Even on Extreme, two slots go unused. The algorithm's fallback for running out of colors never fires on a real board — it exists for a case the current difficulty ladder never reaches.
What the algorithm actually buys, under ordinary vision
Measuring color separation as CIE Lab ΔE (a standard perceptual color-distance metric — roughly, under 10 reads as similar at a glance, above 30 reads as clearly distinct), here is the smallest gap between any two touching regions on a board, averaged across three hundred freshly generated boards per difficulty:
| Board | assignRegionColors | Adjacency-blind (region id → palette slot) |
|---|---|---|
| Easy 5×5 | 36.0 | 28.0 |
| Normal 7×7 | 34.3 | 28.6 |
| Hard 9×9 | 33.5 | 28.1 |
| Extreme 10×10 | 32.5 | 27.5 |
The algorithm adds roughly a fifth more separation, consistently, at every difficulty. To be precise about what that is not: with only five to ten hues drawn from twelve well-spread ones, an adjacency-blind assignment did not produce an outright collision (ΔE under 20) on any of the twelve hundred boards sampled here either — the palette already has headroom to spare at these region counts. What the algorithm removes is luck, not a common disaster. It turns "probably fine" into a designed guarantee — the guarantee now put under a color-vision deficiency.
What a red-green color-vision deficiency changes
Protanopia and deuteranopia, the two common forms of red-green color blindness, together affect roughly 1 in 12 men. Both collapse part of the color space that ordinary trichromatic vision keeps separate — the axis running through red, orange, yellow-green and green — so colors on opposite sides of that axis can end up looking nearly the same.
To measure that on this palette rather than describe it abstractly, this article runs every color through a standard dichromacy simulation (a linear-RGB matrix transform, the same family used by browser and OS accessibility tooling) and re-measures ΔE on the result. This is a full-severity, worst-case bound, not a claim that every colorblind player sees exactly these numbers — most red-green deficiency is a matter of degree, not an all-or-nothing collapse.
The measured result: a collapse on almost every board
Re-running the same three hundred boards per difficulty, taking the closest touching pair again — this time under the simulated deficiency:
| Board | Ordinary vision | Protanopia | Deuteranopia | Boards with a pair under ΔE 12 |
|---|---|---|---|---|
| Easy 5×5 | 36.0 | 10.1 | 18.5 | 97% |
| Normal 7×7 | 34.3 | 9.0 | 16.5 | 97% |
| Hard 9×9 | 33.5 | 8.4 | 15.3 | 99% |
| Extreme 10×10 | 32.5 | 8.1 | 13.5 | 95% |
ΔE 12 is a chosen cutoff, not a hard psychophysical line — it marks the range where two large touching fills would plausibly read as one color rather than two, well short of the separation this palette was designed to hit. Under protanopia the mean closest pair falls to single digits at every difficulty; under deuteranopia it holds up somewhat better but still drops by more than half. Either way, on 95 to 99% of the twelve hundred boards sampled, at least one touching pair ends up below that cutoff.
It's almost always the same pair
That collapse is not spread evenly. Isolating which pair of colors is doing the damage on each affected board, one pair dominates at every difficulty: red touching yellow-green, present in 97 to 99% of boards with any collapse at all. In isolation that pair measures ΔE 54.0 under ordinary vision — genuinely distinct — falling to ΔE 9.5 under protanopia, an 82% drop.
The reason traces to the palette's own ring order. Red and yellow-green sit three slots apart on the twelve-color ring, which `assignRegionColors` treats as a comfortable, non-adjacent gap. But that metric assumes a hue wheel calibrated for ordinary vision, and protanopia/deuteranopia specifically compress the segment of the wheel those two hues sit on opposite ends of — so a gap the algorithm considers safe collapses anyway.
The palette's own source comments also mark a repeating light/mid/dark value band, cycling every three slots — so a gap of exactly three always lands two colors in the same band (purple and teal, a smaller secondary offender, share this too: ΔE 74.0 down to 8.2 under protanopia, the single sharpest drop measured, though it recurs less often). At that ring distance, hue is the only thing telling two colors apart, and hue is exactly what a red-green deficiency erodes.
What actually helps right now
There is no colorblind mode in the game today, and no border between regions to fall back on — this is the reason the problem has nowhere else to hide. The mitigation that genuinely works is outside the game: macOS, Windows, iOS and Android all ship a system-level color-vision-deficiency filter (Settings → Accessibility → Color Filters), and since the problem here is hue compression, a filter tuned to the player's deficiency re-separates the exact hues this measurement shows collapsing. Zooming in does not help — it is a color problem, not a size problem.
If a palette revision ever lands, this measurement says where to start: the ring-distance-3 relationship, not ring-distance-1. Obviously close pairs already get pushed apart and mostly survive the simulation intact. It is the pairs the algorithm currently treats as safely separated that are not.
How this was measured
All figures come from this game's own code: `generatePuzzleSync` for the boards, `assignRegionColors` plus `regionAdjacency` from src/engine/palette.ts for the coloring, run exactly as the live game runs them. Color distance is CIE76 ΔE in Lab space; the color-vision-deficiency simulation applies a standard linear-RGB dichromacy matrix (the Viénot/Brettel family) before converting back and re-measuring. The adjacency-blind comparison maps each region's own numeric id straight to a palette slot, skipping `assignRegionColors` entirely.
These are properties of the palette and generator this game currently ships. This article's test suite re-derives every table from the live modules on each run, so a future palette or generator change fails the build rather than leaving a stale figure live on the page.
Look at the next board this way
Open a fresh board and find the two touching regions that feel closest in color — most boards have one pair worth a second look. That is the palette's safety margin, working as designed. It's also a good page to test a system color filter for red-green vision against, if one is available.
Boards are generated in your browser. Nothing about a game you play is sent anywhere.