Cell by Cell
AboutRings

Techbiopalette 01

Colors Should Have Provenance

We record where data, code, and images come from, yet colors often survive as little more than anonymous hex values. biopalette began with a simple thought: colors should keep their provenance too.

  • Published
  • Reading time8 min
  • LanguagesENZH
  • biopalette
  • R
  • Color
  • Data Visualization
  • Scientific Figures

When I make a figure, color is often where I pause.

The data is ready and the structure of the figure has already been decided. What remains seems like a small question: which colors should I use? Sometimes I open an old script and look for a set of values I have used before. Sometimes I browse a familiar palette collection. More often, I move between several choices that all seem acceptable until one of them finally stops getting in the way.

Colors appear at another kind of moment too. While reading a paper, watching a film, or simply passing an image, I occasionally think: these colors belong together. It would be nice to use them in a figure one day.

So I began keeping them.

At first, what I kept was only a row of hex values. They went into a script, a note, or a temporary file, perhaps under a name such as colors or my_palette, perhaps with no name at all. When I returned later, the colors were still there, but I could no longer remember where they came from, what the original image looked like, or why I had thought they were worth saving.

We carefully record where a dataset came from, which version a piece of code depends on, and whose work an image belongs to. Yet colors often lose their identity the moment they are copied, leaving behind only a few anonymous strings beginning with #.

Over time, that began to feel wrong. Colors should have provenance too.

Keep the colors, and the moment they were seen

The source of a palette is more than a citation placed at the end. It explains why those colors appeared together in the first place.

walter_white comes from the poster for the pilot episode of Breaking Bad: Walter White stands in the New Mexico desert, with sky, scrub, and a bleached horizon behind him. I first read the scene as a cool diverging palette—sky blue at one end, deep olive at the other, with an almost colorless gray-white between them. The palette can later enter a differential-expression heatmap or an effect-size figure, but the relationship among its colors began with the way that sky fell toward the desert.

The same poster admits other readings. Treating the sky, scrub, RV, sand, and smoke as five neighboring objects produces the qualitative palette walter_white2. Reorganizing the scene around rose, warm beige, and green produces another diverging palette, walter_white3. The source does not decide on one correct answer for me, but it makes each choice traceable.

Walter White against the New Mexico sky and desert in the Breaking Bad pilot poster
The poster for the Breaking Bad pilot. walter_white comes from its sky, horizon, and desert.
A five-step diverging palette of blue, gray-white, and olive
Sky blue, gray-white, and deep olive form walter_white's cool reading of the poster.

bcell_clusters, by contrast, comes from a pan-cancer single-cell study of B cells published in Cell. Figure 1B uses 20 colors to distinguish B-cell clusters. biopalette preserves the order of the legend swatches as well as the cell subtypes they originally represented. But those colors did not work alone in the source figure: labels, position, grouping, and dividers also carried information. That matters because it reminds me not to ask color alone to distinguish all 20 categories when the palette moves into another figure.

Twenty B-cell clusters and their color legend in Figure 1B of the paper
Figure 1B from the 2024 Cell paper. Twenty colors work together with cell subtypes, position, and labels.
A twenty-color strip reconstructed from the paper's legend
Reconstructed as bcell_clusters, the values, original legend order, and labels remain together.

A set of values may look beautiful as a strip. Returning to its source reveals the problem it was originally solving. Provenance is not there to give colors a more charming story; it preserves the evidence needed to judge them.

That was the earliest idea behind biopalette. Rather than build another list that collected color values alone, I wanted each palette to remain with its name, type, source, and suitable uses. When I encounter it again, I can know not only which colors I am taking, but what I am taking them from.

From a collection to something usable

Remembering the source is not enough, of course. The colors still have to return to a figure.

I need to find a palette in R by a stable name. I need to know whether it is qualitative, for unordered categories; sequential, for values moving from low to high; or diverging, for values moving away from a meaningful midpoint. I need explicit behavior when too many or too few colors are requested. And I need the palette to work in base R and ggplot2, without copying a vector of values into every script.

Those needs gradually turned the original collection into an R package:

library(biopalette)

get_palette("bcell_clusters", n = 5)
get_palette("three_body")
preview_palette("walter_white")

scale_color_biopalette("three_body")
scale_fill_biopalette_gradient("mitonuclear_blue")

Names matter here. #1991A9 is difficult to remember; walter_white leads the colors back to their source. mitonuclear_blue and mitonuclear_orange describe more than hue: they preserve the association with young and old states in the scientific figure from which they came. A name is not decoration. It is a route from code back to context.

An R package is well suited to answering “how do I call this?”, but not to holding everything. If source images, color tables, example data, reasons for use, and rendered results are all crowded into function documentation, a palette may technically have provenance while that provenance remains difficult to see.

So those materials live in Tessera. There, a palette can remain beside its source material, toy datasets, plotting code, and rendered figures. biopalette is the interface that brings the colors into R; Tessera is where they can pause with their context intact.

Provenance does not vouch for a palette

Keeping a source does not make a palette “correct.”

A film image may create a wonderful atmosphere without offering enough distinction for ten categories. A palette from a paper may work in its original figure and become difficult to read against another background, at another scale, or in another graphical form. Some colors work across large filled areas but converge when reduced to points. Some gradients look smooth without expressing a meaningful midpoint accurately.

Even the source's own use should not be copied blindly. In its original figure, lactate_steps corresponds to five stages of a study, but the lightness of those colors does not change monotonically. It works when the stages are presented as discrete categories. It should not be interpolated into a continuous gradient merely because “stages” sound ordered.

Provenance gives me a place from which to judge again, not a certificate that excuses further checking.

This is why Palette Lab came later: hold the data and graphical structure fixed, change only the palette, and observe what happens when the same colors enter a scatter plot, box plot, stacked chart, survival curve, or heatmap. Harmony in a color strip is only the first impression. Recognition, hierarchy, and emphasis inside a figure are the work the palette ultimately has to do.

Several statistical figures rendered with different palettes in Palette Lab
In Palette Lab, palettes leave the color strip and enter scatter plots, heatmaps, survival curves, and other real figures.

At the other end of this path are smaller tools. When I see an image worth keeping, Palette Extractor helps collect and arrange candidate colors. When those colors need to move between environments, Color Converter translates among HEX, RGB, and alpha-aware representations and produces code that can be taken away directly. They do not change the thought that colors should have provenance; they simply remove a little friction between seeing and using.

What am I really trying to preserve?

Looking back, biopalette certainly preserves colors. Without the hex values, none of the sources or notes could enter code. But what I do not want to lose is the set of relationships present when a color was seen: its relationship to the source image, to the other colors, to a particular data structure, and to the work it once performed in a figure.

Keeping that record also makes me ask more questions when I reuse a palette. Do I like the color itself, or the contrast it formed with its background? Did the original figure distinguish categories through color, or through labels and position as well? Once the palette leaves its original scale, order, and context, what remains?

These questions do not automatically produce a perfect palette. They simply make “it looks good” less than the only reason, and keep a set of colors from having to begin anonymously every time.

biopalette can now be installed directly from CRAN:

install.packages("biopalette")

Going live on CRAN is neither the beginning nor the end of this story. It only means that colors kept from papers, films, and images now have a more stable way into the next figure.

And wherever they travel, I still want to be able to follow their names back to where they first came from.