Color Palette Cinema
Note (2022-11-14): This package no longer is functional because of changes to underlying API cpcinema calls. Thus output is no longer rendered for this blog post.
Friday night I found myself trying to make a plot look pretty. When I’m looking for color palette inspiration I often turn to the Instagram page @colorpalette.cinema for inspiration. Color Palette Cinema is an awesome page that takes stills from films and creates a nice palette of 10 colors from the image. These can range from bright hues, to monochromatic masterpieces. I settled on the Miyasaki masterpiece Spirited Away (streaming on HBO Max, by the way). I extracted the colors with the 3/4 baked package {cpcinema}
that I created right as the pandemic began at the end of March. (Thinking back, it’s slightly wild that I wrote this package sitting inside of the Cambridge library without a mask.)
Around midnight, I tossed a silly #rstats tweet into the void thinking nothing of it—as I usually do.
The most wonderful #rstats package I've ever made for myself. pic.twitter.com/o4C3g0Mtpj
— jo-sigh-ughhh (@JosiahParry) March 13, 2021
I awoke to what felt celebrity levels of likes and interactions on the tweet. As of right now, there are 343 likes on the tweet. That’s 340 more likes than I’m used to. Apparently people thought this package idea was fun. I was surprised. One of the first replies stuck out to me the most:
Beautiful and private :(
— Emil Hvitfeldt (@Emil_Hvitfeldt) March 13, 2021
It hadn’t crossed my mind that others would want to utilize this package—hence why I didn’t share the package URL. The package is public in fact. But I’ve not once publicized it until this post.
Why didn’t I “release” cpcinema?
There are two distinct reasons why I didn’t tweet the package into the void.
The first reason is that I struggled for hours on end trying to figure out an easy way to get the color palettes directly from an Instagram post. I am only familiar with one endpoint of an undocumented Instagram API and that only returns the URL for the first image in a post. I want all of them. I then attempted to do this via the official Facebook Instagram API—a truly insurmountable task for those seeking simple GET
requests for data. The Instagram Basic API is intended for those who are seeking to build true applications—not for data scientists. The good news is that I’ve figured this out. h/t to Cole Arendt for teaching me how to use the networking tab of Chrome. We wouldn’t be here without him.
The second reason is less technical. The functionality I was (and am) most excited about creating was 1) an S3 vectors class using {vctrs}
; 2) a printing method that displayed the colors; and 3) color interpolation for continuous color scales. As I was nearing completion of this package I learned about Emil Hvitfeldt’s awesome package {paletteer}
. paletteer accomplishes 1) and 2) through the dependency {prismatic}
which Emil, also created—a prolofic software engineer if there ever was one. (Though I will stand on my petty hill that I prefer my printing method 😉, it’s something about the squares and the vertical orientation—except it gets out of hand with like 20+ colors.) And 3) is accomplished by paletteer itself.
Using cpcinema
cpcinema is a rather somewhat simple package to use!
First things first: install the package.
# install.packages("remotes")
remotes::install_github("JosiahParry/cpcinema")
Next, find a post from Color Palette Cinema that you really like and grab the URL.
This post with an image of Blade Runner is awesome.
So I’ll grab the URL and pass it to pal_from_post()
library(cpcinema)
library(tidyverse)
blade <- pal_from_post("https://www.instagram.com/p/CEuS3Y_oEbw/")
Unfortunately R Markdown doesn’t render the beautiful printing. But try it out for yourself!
# this is stored in a list because sometimes a post
# has more than one color palette
(pal <- blade[[1]])
Now we can use the palette for plotting! The below example takes the built in object USArrests
and samples 10 random states and creates a ranked bar chart colored by state.
as_tibble(USArrests, rownames = "state") %>%
sample_n(10) %>%
mutate(state = fct_reorder(state, UrbanPop)) %>%
ggplot(aes(UrbanPop, state, fill = state)) +
geom_col() +
theme_light() +
labs(title = "States ranked by urban population") +
# This is where we add the palette
scale_fill_manual(values = pal)
You can even use the color palettes for continuous data. The function color_palette()
will create a color palette from a vector of color codes. If n
is greater than the number of colors provided, color interpolation will be done to create a smooth palette!
Below we create a heat map with the built it object volcano_df
. First we create the new color palette with 100 values, then create the heatmap. The important line is scale_fill_gradientn()
!
continuous_pal <- color_palette(pal, n = 100, "continuous")
ggplot(volcano_df, aes(x = id, y = y, fill = z)) +
geom_tile() +
scale_fill_gradientn(colours = continuous_pal) +
scale_x_discrete(expand = c(0, 0)) +
scale_y_discrete(expand = c(0, 0)) +
coord_equal() +
theme_void()
What next?
Next, I’ll work to make this package work nicely with prismatic
and paletteer
. The challenge with the latter is that you can’t provide character strings directly to the scale_color/fill_paletteer_d/c()
functions directly. They have to be provided as pkgname::palette
. I am not too sure how to incorporate cpcinema in there. But I think my preference would be to provide a character vector as that is more flexible.