Eleven stones, one glance

Our first palette looked fine on screen and measured terribly. What fixed it was not a better colour picker. It was measuring the colour the player actually sees instead of the colour we specified, which in our game turned out to be two different colours.

The problem with eleven tiers

Pebble Merge has eleven stone tiers. Sand fuses into clay, clay into moss, and so on up to sunstone. The player has to tell any two of them apart instantly. Two identical stones are the thing you are looking for, and two different ones are the thing you are trying not to confuse them with. We did not appreciate how strict that requirement is until we watched ourselves hesitate over a pair of them.

What makes this harder than it sounds is that the tiers do not arrive in order. Only the five smallest are ever dropped. We spawn tiers 0–4, weighted 34 / 26 / 20 / 13 / 7 percent so the board stays playable. The larger ones are built by merging, so by the middle of a game any two tiers in the set can end up sitting next to each other, and a palette that reads cleanly when the eleven are listed in a column can fall apart completely once tier 2 and tier 10 are touching.

Size is not much help either. Tier 0 has a radius of 15 pixels and tier 4 has 37, which sounds like a wide range until you remember that both are on screen at once, both are roughly circular, and the player is scanning for colour rather than for diameter. Counting pixels to identify a stone means the scan has already failed. We found that out the slow way, by watching our own eyes go back and forth between two grey blobs we were supposed to recognise.

The mistake: trusting the swatch

We picked the first palette by eye, in a colour picker, and liked it. I liked it quite a lot at the time. Then it went through the art layer, which desaturates lower tiers to make them look like rough, unpolished stone and adds saturation and specular highlights as the tiers rise. That transform is the entire visual point of the game: stones look progressively tumbled and polished. It also quietly destroyed the palette.

By the time a stone reaches the screen it is not the colour in our source file. It is a desaturated, speckled, lit version of it, and the amount of desaturation varies by tier. The four smallest stones, the ones the player deals with most, came out as near-identical greys. When we finally measured the rendered result, the minimum distance between adjacent tiers was ΔE00 = 3.2. Anything under 5 is not reliably distinguishable at a glance, or so the usual guidance goes. So the complaint we had been hearing, “the first four look the same”, was not fussy players. It was correct, and we had been quietly blaming the players for it.

We did not conclude that we should choose better colours. We concluded that we should be measuring the thing the player sees. A design swatch is an input, and we had been reading it as though it were the output, which is how the bug survived as long as it did.

What we measure now

We convert each rendered stone colour to CIELAB and compute the difference between tiers using CIEDE2000, a perceptual colour-difference formula that tries to put numbers on how different two colours look to a human rather than on how far apart they sit in RGB space. Ten units of RGB distance means nothing to anybody. ΔE00 at least tries to mean something, and for our purposes the gap between nothing and an attempt was worth the extra code.

The test prints the whole ladder, including lightness (L*), chroma (C*) and hue angle, because a failure we can read is worth more than a failure that is merely red:

TierNameSpecifiedRenderedL*C*h°ΔE vs previous
0Sand#dbd4c2#d9d4c785794n/a
1Clay#c17e57#b4826559285626.1
2Moss#b4c78e#b6c498772412229.3
3Jade#45a188#519b87592817320.8
4Celestite#8aafd6#8faecf702026225.3
5Sapphire#535ec6#565fb9445429525.4
6Amethyst#aa7bcc#a77dc6594331319.8
7Garnet#c96493#c46792564335115.3
8Topaz#daa262#d8a26671417238.6
9Aquamarine#53c6c6#55c5c5743319737.9
10Sunstone#eddba1#eddba188319431.6

Three thresholds, not one

A single “adjacent tiers must differ by X” rule would not have caught our bug, because there are three different ways two stones can be compared, and they have different requirements:

Designing for colour-vision deficiency

Roughly one in twelve men has some form of colour-vision deficiency, which is far too many people to design around by accident, so hue alone is not a safe channel. Our third assertion is about lightness. We require lightness to alternate up and down as tiers rise, so that even with hue and chroma flattened, consecutive tiers still differ in brightness.

Looking at the L* column, the ladder runs 85, 59, 77, 59, 70, 44, 59, 56, 71, 74, 88. It goes down, up, down, up, instead of drifting monotonically in one direction, and that zigzag is deliberate. A palette that simply got lighter as the tiers increased would collapse for anyone who cannot see the hue shifts, because all the small tiers would crowd into one narrow brightness band and stay there. Eight of the ten steps alternate as intended. I would prefer ten of ten, and we are not there. The two that do not are close enough that lightness still works as a usable second channel throughout, which is the standard I am willing to ship against.

What we would tell someone doing this

None of this is clever, which is rather the point. The palette problem went away once we wrote down what “distinguishable” meant in numbers and then refused to ship anything that failed them. Writing the test took an afternoon. The argument it settled had already run longer than that, and I am mildly annoyed that we spent the time arguing when we could have been measuring.

← Back to the blog