library(qualpalr)
pal <- qualpal(4, "Vermeer:PearlEarring", bg = "white")
pal$hex[1] "#80a0c7" "#100f14" "#a65141" "#b1934a"
My pet project Qualpal is now a stand-alone C++ library as well as web app and was just published in the Journal of Open Source Software!
Johan Larsson
22 October 2025
It is now nine years since I first blogged about my pet project Qualpal (at that point called qualpalr), which is an R package for generating qualitative color palettes for data visualization in R. It was motivated by the fact that almost all color palettes were designed for a fixed number of categories and therefore must be suboptimal for other numbers of categories.
At the time, there were only a couple of other alternatives available and out of these I only knew about iWantHue, which is a web app that generates these kinds of color palettes. And while iWantHue is great and I think has raised the issue of generating these palettes to more prominence, I felt that there were opportunities to improve upon it, which is why Qualpal was born.
The major innovations of Qualpal were
Another, but perhaps obvious, addition was that Qualpal was accessible directly from R, which is where I (at least at the time) did most of my work. At the time, it was the first package in R to provide this type of automatic qualitative palette generation.
I have made little progress on the package until the start of this year when I decided that it was time to make the investment to turn Qualpal into a more widely accessible tool. I had the following ideas in mind.
All of the above has now been implemented. I’ve decoupled the C++ parts of qualpalr into a separate library called Qualpal, which lives in its own repository on GitHub.
The new C++ library is designed to be lean and carries no other dependencies than the C++ standard library, although there is support for multi-threading via OpenMP (if available).
The functionality of the package is mostly concentrated in the Qualpal class, which uses a builder-type interface to customize palette generation. Here’s an example, where we construct a five-color palette from part of the hue-saturation-lightness (HSL) color space as input.

In the next example, we change to using one of the built-in color palettes, in this case Set2 from ColorBrewer (Harrower and Brewer 2003) as input, and adapting the color palette to the color vision deficiency type deuteranomaly of severity 0.7 (70%).

Extensive documentation, including a complete API reference, is available at jolars.github.io/qualpal.
The R package, qualpalr, now uses the C++ library as a backend, and new releases of the C++ library are semi-automatically integrated into the R package. Only the R-specific parts remain in the qualpalr package, which means that development is now focused on the C++ library directly.
The main major change of the package is a switch in the character method of the main function of the package, qualpal(), which now assumes that the second argument is the name of a built-in palette. But backwards compatibility with the old behavior1 is maintained by selectively handling the previous set of allowed input.
1 Where the argument defined a subspace of the HSL color space.
In the following example, we generate a 4-color palette by picking colors from Vermeer’s Girl with a Pearl Earring. This example also showcases a new feature that allows users to optimize the palette against a given background color (in this case white).
[1] "#80a0c7" "#100f14" "#a65141" "#b1934a"
The default plot method shows a multi-dimensional scaling (MDS) plot of the colors in the color difference metric space, which gives an idea of how well-separated the colors are.
Another addition to the qualpalr package, which is relevant irrespective of how you choose to use Qualpal, is a new vignette on comparisons to alternatives. So, in case you are interested in knowing how Qualpal compares to other alternatives, such as palettailor (Lu et al. 2021), Colorgorical (Gramazio, Laidlaw, and Schloss 2016), the aforementioned iWantHue, or glasbey (McInnes 2025), please check it out. As you might guess, Qualpal performs very well in these comparisons.
As a result of refactoring the package into a pure C++ library, it was also straightforward to create a command-line interface (CLI) tool for Qualpal, relying on the excellent CLI11 library. This CLI tool is only available from source as of now, but I hope it will be packaged for various Linux distributions in the future.
Here’s an example of generating a 5-color palette from the HSL colorspace:

Please see the instructions in the README for how to build and use the CLI tool.
One of the major motivations for refactoring Qualpal into a C++ library was to make it possible to serve it as a static web app by compiling the C++ code to WebAssembly (WASM). Although I love how easy it is to turn R code into web apps using Shiny, I have always been bothered by the hoops you have to jump through to deploy and manage Shiny apps, whereas the new Qualpal web app is hosted on GitHub pages as a static site and is served directly upon successful builds from GitHub Actions.
Running a server instance is also not free, so hosting this as a static web app means significant cost savings and automatically scales to any number of users.

Finally, I am happy to announce that Qualpal was just published in the Journal of Open Source Software (JOSS)! (Larsson 2025) I have for a long time been interested in submitting something to JOSS, which to me seems to have a great model for papers about software.
First, I think the reviewer system, which is entirely based around GitHub issues, is natural for a journal about software, since much software anyway is located in a public repository, and issues can be cross-referenced seamlessly. It is also completely transparent. Finally, I like the fact that papers are short, motivated by the fact that the software’s documentation should serve as the source of information about the software, rather than an academic paper (which is fixed).
Another change that was made as part of the recent work on Qualpal was the introduction of other color difference metric. In particular, Qualpal now supports the CIEDE2000 metric (Sharma, Wu, and Dalal 2005), which is the current standard set by the CIE organization for computing differences between colors. Qualpal previously used the DIN99d metric (Cui et al. 2002), which is computationally appealing since, once the colors are converted into the DIN99d space, the color metric formula is simply Euclidean distance. But the tradeoff is that DIN99d is not as perceptually accurate, and I therefore decided o switch the default to CIEDE2000.
The result is that the palettes are now more distinct than before, at the cost of an increase in computation time. For most practical purposes, however, the difference in speed is negligible and Qualpal is still fast enough for this not to be even noticeable.
Coming back to the start of this post and the motivation for refactoring Qualpal as a stand-alone C++ library, this change means that it is now easier to create new bindings for Qualpal. The most obvious next step is therefore to create a Python package that wraps around Qualpal. Doing so should be quite straightforward and I will eventually do this myself, unless someone gets there first!
I am also happy for any kind of feedback regarding Qualpal, so please check out the GitHub repository for the project, where you can file issues or open up discussions should you have any ideas on how the project could be improved or if you would like to contribute directly, which would be very much appreciated.