Skip to contents

Color palettes inspired by paintings of Candido Portinari in the Museu de Arte de Sao Paulo (MASP) collection and by his Guerra and Paz panels at the United Nations Headquarters in New York. Fast to use, easy to plot with, and built for ggplot2.

Installation

# install.packages("remotes")
remotes::install_github("rafalopespx/PortinariColors")

Quick start

library(PortinariColors)
library(ggplot2)

portinari.pal("cafe")                         # full palette as hex codes
portinari.pal("retirantes", n = 3)            # 3 colors: both ends and the center
portinari.pal("guerra_azul", n = 20, type = "continuous")  # interpolated

# Qualitative: unordered groups
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
  geom_point(size = 3) +
  scale_color_portinari("cafe", name = "Cylinders")

# Sequential: ordered values
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
  geom_raster() +
  scale_fill_portinari("guerra_azul", type = "continuous", direction = -1)

# Diverging: values around a midpoint
ggplot(mtcars, aes(wt, mpg, colour = mpg - mean(mpg))) +
  geom_point(size = 3) +
  scale_color_portinari("guerra", type = "continuous")

Discrete palettes pick colors to suit the palette style: the most distinct colors for qualitative palettes, evenly spaced steps for sequential palettes, and balanced steps either side of the light center for diverging palettes. Continuous palettes interpolate the full palette in CIELAB.

Main functions

Function Purpose
portinari.pal() Get a palette as hex colors (discrete or continuous)
scale_color_portinari() / scale_colour_portinari() ggplot2 color scale
scale_fill_portinari() ggplot2 fill scale
theme_portinari() ggplot2 theme with a Portinari canvas, in three moods
portinari.palettes() Table of palettes by style, with colorblind status
portinari.works() The paintings behind each palette, with links
portinari.cvd.check() Colorblind simulation for a palette
show_portinari() Draw palettes as swatches

portinari.pal() and the scale functions share the same arguments: type = "discrete" | "continuous", direction = 1 | -1, and colorblind = TRUE | FALSE.

A Portinari theme

theme_portinari() dresses a plot like one of Portinari’s canvases: a warm ground of raw linen, firm drawn axis lines instead of a boxed panel, earth-colored lettering and a thin painted frame. Two more moods follow his United Nations panels: the deep indigo of Guerra and the golden light of Paz.

ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
  geom_point(size = 3) +
  scale_color_portinari("paz", name = "Cylinders") +
  labs(title = "Fuel economy", subtitle = "Heavier cars travel less per gallon") +
  theme_portinari()                 # or theme_portinari("guerra"), ("paz")

Options: grid = "y" | "x" | "xy" | "none", frame = TRUE | FALSE, and the usual base_size and base_family. All text meets WCAG AA contrast in every mood. On a light mood, a palette’s lightest color sits close to the ground, so keep it for filled areas rather than points or lines; on the guerra mood, prefer palettes other than guerra and guerra_azul, whose darkest color is the ground itself.

Palettes

Palette Style Colors Colorblind-safe Works
cafe qualitative 6 all 6 Coffee Agricultural Worker; Saint Francis
paz qualitative 8 4 Paz (Peace), UN Headquarters panel
retirantes diverging 8 all 8 Northeastern Migrants; Dead Child; Bust of the Poet Felippe d’Oliveira
inah diverging 5 4 Portrait of Ináh Prudente de Moraes
sofia diverging 5 3 Portrait of Sofia Cantalupo
terra_roxa diverging 9 all 9 Coffee Agricultural Worker
guerra diverging 9 all 9 Guerra (War), UN Headquarters panel
guerra_azul sequential 6 all 6 Blue scale of Guerra
paz_ouro sequential 4 all 4 Earth-to-gold run of Paz
roxa_terra sequential 8 all 8 Coffee Agricultural Worker; Guerra (War)

See The paintings for images and links to every work, and Choosing a palette style for when to use each style.

Palettes are merged when they are too similar. Candidate palettes were compared by mean CIELAB distance. Two groups were merged this way: the migrant-series works with the ‘Felippe d’Oliveira bust’, and the ‘Coffee Agricultural Worker’ with ‘Saint Francis’. guerra and paz are distinct from every other palette (closest: paz vs cafe, dE 18.9), so they stay separate. guerra_azul, paz_ouro and roxa_terra are sequential companions of guerra, paz and terra_roxa and are kept on purpose: they share colors with their diverging or qualitative parent but serve a different kind of data.

Use colorblind = TRUE to keep only the colorblind-safe colors of a palette:

portinari.pal("inah", colorblind = TRUE)      # 4 safe colors
portinari.cvd.check("sofia")                  # see where the palette breaks down

Important: colors are estimates

The hex values are hand-estimated from the dominant tones of each painting. They were not sampled from the MASP images. guerra and paz follow published descriptions of the panels: Guerra as “a symphony in blue” of deep violet tones over light blues tending to pale citrine, with an orange-to-red counterpoint, and Paz as dominated by golden yellows. Treat them as a starting point. data-raw/extract_masp_palettes.R samples the originals when the MASP site is reachable, and data-raw/palette_selection.py reproduces the merge and colorblind analysis.

The exceptions are terra_roxa and roxa_terra, whose colors were sampled from the MASP image of Coffee Agricultural Worker (data-raw/terra_roxa.py). roxa_terra ends on the pale citrine of guerra_azul, reached through two interpolated tints.

Colorblind safety

A palette is colorblind-safe for its first n colors when those colors stay at least CIE76 dE 10 apart under protanopia, deuteranopia and tritanopia simulation Machado et al., 2009. The simulation is in portinari.cvd.check().

Contributing

Add a palette by appending an entry to .portinari_palettes in R/palettes.R. Order the colors by priority, then set cvd_n by running portinari.cvd.check() on the new palette.

Credits

Works by Candido Portinari (1903-1962). Images and metadata are from the Museu de Arte de Sao Paulo (MASP) collection. The palettes are an inspired interpretation, not an official MASP product.

The package API follows the conventions of MoMAColors and MetBrewer.