Why Now Playing Cards Come Out Grey
A now playing card is two colour decisions and a bit of layout. Both decisions are usually made by a single line of code, and both are wrong in ways you can measure in about a second each.
The first line takes the background from the average of the cover art. The second writes the text in white. Here is what each one actually does.
The average of a picture is a colour the picture does not contain
Take six equal bands of fully saturated hues — red, yellow, green, cyan, blue,
magenta — and average every pixel. The answer is rgb(128, 128, 128).
Saturation 0.0. Exactly grey, from an image with no grey in it
anywhere.
That is not a quirk of one contrived picture. We measured three:
| Cover | Average | Saturation | Present in the artwork? |
|---|---|---|---|
| Six saturated hues | 128, 128, 128 | 0.0 % | No |
| Half red, half blue | 128, 0, 128 | 100 % | No |
| Green over near-black | 27, 103, 52 | 58.5 % | No |
In all three the average colour was absent from the artwork. Half red over half blue averages to purple; there is no purple in the picture. Green over near-black averages to a darker, duller green than the green that is there.
And the pattern behind it is the part that matters: the more colourful the cover, the greyer its average, because opposing hues cancel. A bold sleeve gets a dull card. A monochrome sleeve gets a faithful one. That is precisely backwards from what anyone wants, and it is why so many of these cards look like the same washed-out rectangle regardless of what you fed them.
What to pick instead
The fix is to choose a colour that is genuinely in the picture. Quantise the cover into buckets — sixteen levels per channel is plenty — count the pixels in each, and take a populous one, weighted toward saturation so the sleeve's white paper and its black shadow do not win by sheer area.
On the six-hue cover that returns a red the picture actually contains. On a real sleeve it returns the colour you would have pointed at. The difference is one pass over a 96 × 96 thumbnail, which costs nothing.
White text fails on nearly two thirds of colours
Now the second line. Whatever colour came out of step one, the text goes on in white.
We sampled 636,056 colours across the sRGB cube and computed the WCAG contrast ratio of each against white and against black. 4.5:1 is the threshold for normal body text:
| Text colour | Clears 4.5:1 | Clears 3.0:1 |
|---|---|---|
| Always white | 36.4 % | 56.9 % |
| Always black | 65.3 % | — |
A card that always writes in white is below the readability threshold on 63.6 % of the colours it could land on. Not unreadable in a dramatic way — it is legible on your own screen, at full size, when you already know what it says. It stops being legible on someone else's phone, in daylight, at thumbnail size, which is the only place it will ever actually be seen.
You never have to lose this one
Here is the part worth knowing, and it is stronger than a heuristic.
We scanned all 16,777,216 sRGB colours — the entire space, not a
sample — and took, for each colour, the better of its contrast against white and
against black. The worst case in the whole cube is 4.58:1, at
rgb(207, 13, 204).
4.58 is above 4.5. There is no background colour on which both white and black text fail the threshold. Choosing between them per colour is not a rule of thumb that usually works; it is readable by construction, everywhere, and it costs one comparison:
function pickText(bg) {
return contrast(bg, WHITE) >= contrast(bg, BLACK) ? WHITE : BLACK;
}
The measured figure is not an artefact of the scan, either. It matches the closed form exactly. Contrast against white falls as the background lightens and contrast against black rises, so the worst case sits where the two curves cross — at relative luminance √(1.05 × 0.05) − 0.05 = 0.1791, giving 1.05 / 0.2291 = 4.5826. The exhaustive scan and the algebra agree to four decimal places.
The third small thing
While we are here: track titles are long, and most generators let them run off the
edge of the card or shrink the font until the artist line looks like a different
design. A canvas can measure text — ctx.measureText(s).width — so the
honest behaviour is to measure, ellipsise at the width available, and leave the type
size alone. It is three lines and nobody does it.
What to do when you make one
- Take the card colour from the artwork, not from an average of it. The average is usually a colour the artwork does not contain, and the more colourful the sleeve the greyer it gets.
- Pick the text colour by contrast rather than by habit. It cannot fail: the worst case across the whole colour space is 4.58:1.
- Check the number rather than your eye. You know what the title says, which makes you the worst possible judge of whether it is readable.
- Measure long titles and ellipsise them, instead of letting them run past the edge or shrinking the type.
Try it
Our now playing card maker does all of the above and shows you the second card beside yours — average colour, white text, the usual way — so the difference is visible rather than asserted. It reports the contrast ratio it achieved and the one white text would have given. Everything happens in your browser; the cover art is never uploaded.
If the cover is not square yet, the image resizer will get it there first, and the safe zone checker shows what a story's own interface will cover once you post the card.
FAQ
Why does averaging a cover give grey?
Because opposing hues cancel. Red and cyan average to grey, as do yellow and blue; a cover with a spread of saturated colours averages to something near the middle of the cube. Six equal bands of saturated hues gave exactly rgb(128, 128, 128), saturation 0.0.
Is 4.5:1 a real threshold or a guideline?
It is the WCAG AA threshold for normal-size body text, and it is a guideline in the sense that nothing enforces it on a social post. It is also the point below which text measurably costs people reading effort — more of them, and more effort, the further below it you go.
Does picking between white and black really never fail?
Not anywhere in sRGB. We scanned all 16,777,216 colours and the worst case for the better of the two is 4.5826:1, which matches the closed form: the curves cross at luminance sqrt(1.05 x 0.05) - 0.05 = 0.1791. Above 4.5, everywhere.
What about coloured text instead of white or black?
It can look better and it is harder to guarantee — a tinted text colour has to be checked against the background the same way, and most of the tints people reach for sit close to the background they were derived from. Black and white are the two colours whose worst case you can prove.
Why quantise rather than take the single most common pixel colour?
Because photographs and printed sleeves rarely repeat an exact RGB triple often enough for a single value to be meaningful. Quantising to sixteen levels per channel groups near-identical pixels together so the count means something, and weighting by saturation stops the paper white or the shadow black from winning on area alone.