Steal like an Rtist: Creative Coding in R

Homage to the Square
Josef Albers

Ijeamaka Anyene Fumagalli and Sharla Gelfand

posit::conf(2023)

Homage to the Square – Ascending, 1953

Josef Albers, 1966, by Yousuf Karsh

 

Homage to the Square – Ascending, 1953

Creating a rectangle in {ggplot2}

geom_rect()

Use arguments to map data to visual properties

  • xmin, xmax: minimum and maximum x values
  • ymin, ymax: minimum and maximum y values
  • fill: color

Creating a rectangle in {ggplot2}

geom_rect()

library(ggplot2)

ggplot() +
  geom_rect(
    aes(
      xmin = 3, xmax = 7,
      ymin = 1.5, ymax = 5.5
    ),
    fill = "#F5BB1D"
  )

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage

Recreate

library(dplyr)

homage <- tribble(
  ~x0, ~y0, ~size,    ~color,
    0,   0,    10, "#5A9CBA",
    1, 0.5,     8, "#919EA3",
    2,   1,     6, "#F1EFDF",
    3, 1.5,     4, "#F5BB1D"
  ) %>%
  mutate(
    x1 = x0 + size,
    y1 = y0 + size
  )

homage
# A tibble: 4 × 6
     x0    y0  size color      x1    y1
  <dbl> <dbl> <dbl> <chr>   <dbl> <dbl>
1     0   0      10 #5A9CBA    10  10  
2     1   0.5     8 #919EA3     9   8.5
3     2   1       6 #F1EFDF     8   7  
4     3   1.5     4 #F5BB1D     7   5.5

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  ))

homage_p

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  ))

homage_p

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  )) +
  scale_fill_identity()

homage_p

scale_fill_identity() tells ggplot2 the variable in color is a color value it can use directly, not a categorical variable that needs to be represented by a fill color

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  )) +
  scale_fill_identity() +
  coord_fixed()

homage_p

coord_fixed() sets a fixed coordinate system, with an aspect ratio of 1, to ensure one unit on the x-axis is the same as one unit on the y-axis (aka: a square is a square)

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  )) +
  scale_fill_identity() +
  coord_fixed() +
  theme_void()

homage_p

theme_void() is a completely empty theme, creating a blank canvas for us to work on

Recreate

homage_p <- ggplot(homage) +
  geom_rect(aes(
    xmin = x0,
    xmax = x1,
    ymin = y0,
    ymax = y1,
    fill = color
  )) +
  scale_fill_identity() +
  coord_fixed(expand = FALSE) +
  theme_void()

homage_p

coord_fixed() with expand = FALSE removes any margin

Save your work

ggsave("homage.png",
  homage_p,
  bg = "black"
)

vs according to the actual dimensions of the work

ggsave("homage_square.png",
  homage_p,
  width = 5,
  height = 5,
  bg = "black"
)

Recreation done!

Homage to the Square – Ascending, 1953

Homage to the Square – Ascending, recreation, 2023

What is generative art?

“Generative art operates within a rule structure but has an element of chance that is crucial to what many artists enjoy about it. The final work is partly produced by an autonomous system, which may be strictly regulated by the rules or operate within parameters.”

— Charlotte Kent, Beyond the Janus-Faced Typologies of Art and Technology

What is generative art?

Remix time! Creating a small system

make_homage <- function(colors = c("#5A9CBA", "#919EA3", "#F1EFDF", "#F5BB1D")) {
  tribble(
   ~x0, ~y0, ~size,
     0,   0,    10,
     1, 0.5,     8,
     2,   1,     6,
     3, 1.5,     4
   ) %>%
    mutate(
      x1 = x0 + size,
      y1 = y0 + size,
      color = sample(colors, size = 4)
    ) %>%
    ggplot() +
    geom_rect(aes(
      xmin = x0, xmax = x1,
      ymin = y0, ymax = y1,
      fill = color
    )) +
    scale_fill_identity() +
    coord_fixed() +
    theme_void()
}

Remix time! Creating a small system

make_homage()

make_homage()

set.seed()

Setting a seed controls the state of the random number generator in R

set.seed(1234)

make_homage()

set.seed(1234)

make_homage()

Your turn!

  1. Open the file exercises/02-homage-to-the-square/exercise-1.Rmd
  2. Update the function make_my_homage() to take a seed argument, and set the seed in the body of the function
  3. Verify that running make_my_homage() with the same seed creates the same output, and that with a different seeds creates different outputs
  4. Share an output and the seed that created it in the GitHub discussion for this exercise
05:00

Composition

Deliberately simple and consistent in structure, the nested squares were a platform for exploring color.

Homages are all about mutability of color relationships and the malleability of subjective perceptions of colors.

Planes and Frames: Spatial Layering in Josef Albers’ Homage to the Square Paintings, J. Mai, 2016

Color

Hex

An opaque but common format of representing color

library(scales)

palette <- c("#4d5a0b", "#f37f92", "#f49346", "#ffcd84")

show_col(palette)

“#5A9CBA” is one of the colors we used before - what is it?

RGB

How much red, green, and blue make up a color (0 to 255)

mystery_color <- "#5A9CBA"

col2rgb(mystery_color)
      [,1]
red     90
green  156
blue   186

HSL

Hue

Color on the color wheel

(0 to 360)

Saturation

Vividness, how much of the color is actually present

From grey to full saturation of the color (0% to 100%)

Lightness

How light/bright the color is

From black to white (0% to 100%)

Hue

Hue

library(prismatic)

mystery_color <- color(mystery_color)

color(mystery_color) %>%
  clr_extract_hue()
[1] 198.75

Hue

mystery_color %>%
  clr_rotate(degrees = 100)
<colors>
#C17FAAFF 

Saturation

mystery_color %>%
  clr_extract_saturation()
[1] 41.02564


saturated_color <- mystery_color %>%
  clr_saturate(shift = 0.5)

desaturated_color <- mystery_color %>%
  clr_desaturate(shift = 0.5)

c(saturated_color, mystery_color, desaturated_color) %>%
  color() %>%
  plot()

Saturation

Desaturation generally makes things more interesting… compare to our palette in full saturation

homage[["color"]] %>%
  color() %>%
  clr_saturate(shift = 1) %>%
  plot()

Lightness

mystery_color %>%
  clr_extract_lightness()
[1] 54.11765


lighter_color <- mystery_color %>%
  clr_lighten(shift = 0.2)

darker_color <- mystery_color %>%
  clr_darken(shift = 0.2)

c(lighter_color, mystery_color, darker_color) %>%
  color() %>%
  plot()

Back to the Homages

homage_hsl <- homage %>%
  mutate(
    color = color(color),
    h = clr_extract_hue(color),
    s = clr_extract_saturation(color),
    l = clr_extract_lightness(color)
  ) %>%
  select(color, h, s, l)

homage_hsl
# A tibble: 4 × 4
  color         h     s     l
  <colors>  <dbl> <dbl> <dbl>
1 #5A9CBAFF 199.  41.0   54.1
2 #919EA3FF 197.   8.91  60.4
3 #F1EFDFFF  53.3 39.1   91.0
4 #F5BB1DFF  43.9 91.5   53.7

Choosing color

Color in data visualization has constraints - representing distinct categories, accessibility

Color in art is different - what emotion are you trying to evoke? What are you trying to draw attention to? What period are you referencing?

How do you know what looks good?

Monochromatic

Multiple tones of one color

Study for Homage to the Square, 1972

“If one says ‘red’ (the name of a color) and there are 50 people listening, it can be expected that there will be 50 reds in their minds. And one can be sure that all these reds will be very different.”

— Josef Albers, Interaction of Color (1963)

Study to Homage to the Square - Endless, 1964

Monochromatic

monochromatic <- tribble(
  ~color,
  mystery_color,
  mystery_color %>% 
    clr_lighten(shift = 0.20),
  mystery_color %>% 
    clr_lighten(shift = 0.40),
  mystery_color %>% 
    clr_lighten(shift = 0.60)
)

plot(monochromatic[["color"]])

make_homage(
  seed = 1,
  colors = monochromatic[["color"]]
)

Analogous

Three colors side by side on the color wheel

Homage to the Square: Edition Keller Ia, 1970

Analogous

analogous <- tribble(
  ~color,
  mystery_color %>% 
    clr_lighten(shift = 0.20),
  mystery_color,
  mystery_color %>% 
    clr_rotate(degrees = 30),
  mystery_color %>% 
    clr_rotate(degrees = 60)
)

plot(analogous[["color"]])

make_homage(
  seed = 2,
  colors = analogous[["color"]]
)

Analogous

That was a bit ugly - add contrast in other ways

analogous_better <- tribble(
  ~color,
  mystery_color %>% 
    clr_lighten(shift = 0.50),
  mystery_color,
  mystery_color %>% clr_rotate(degrees = 30) %>%
    clr_lighten(shift = 0.25),
  mystery_color %>% clr_rotate(degrees = 60) %>%
    clr_darken(shift = 0.25)
)

plot(analogous_better[["color"]])

make_homage(
  seed = 3,
  colors = analogous_better[["color"]]
)

Complementary

Two colors on opposite sides of the color wheel

Homage to the Square: Earth and Air, 1965

Complementary

monochromatic_mini <- tribble(
  ~color,
  mystery_color %>% clr_lighten(shift = 0.20),
  mystery_color
)
complementary <- bind_rows(
  monochromatic_mini,
  monochromatic_mini %>%
    mutate(
      color = clr_rotate(color,
        degrees = 360 / 2
      )
    )
)
plot(complementary[["color"]])

make_homage(
  seed = 3,
  colors = complementary[["color"]]
)

Complementary

Having the same saturation and lightness doesn’t always work!

monochromatic_mini <- tribble(
  ~color,
  mystery_color %>%
    clr_lighten(shift = 0.75),
  mystery_color
)

complementary <- bind_rows(
  monochromatic_mini,
  monochromatic_mini %>%
    mutate(
      color = clr_rotate(color,
        degrees = 360 / 2
      ),
      color = clr_desaturate(color,
        shift = 0.5
      )
    )
)

plot(complementary[["color"]])

Complementary

Having the same saturation and lightness doesn’t always work!

make_homage(
  seed = 3,
  colors = complementary[["color"]]
)

Complementary

Triadic

Three colors evenly spaced on the color wheel

Triadic

triadic <- tribble(
  ~color,
  mystery_color %>% 
    clr_lighten(shift = 0.20),
  mystery_color,
  mystery_color %>%
    clr_rotate(degrees = 360 / 3),
  mystery_color %>%
    clr_rotate(degrees = 2 * 360 / 3)
)

plot(triadic[["color"]])


make_homage(
  seed = 5,
  colors = triadic[["color"]]
)

Triadic

triadic <- tribble(
  ~color,
  mystery_color %>% 
    clr_lighten(shift = 0.50),
  mystery_color,
  mystery_color %>%
    clr_rotate(degrees = 360 / 3) %>%
    clr_lighten(0.25),
  mystery_color %>%
    clr_rotate(degrees = 2 * 360 / 3) %>%
    clr_saturate(0.15)
)

plot(triadic[["color"]])

make_homage(
  seed = 5,
  colors = triadic[["color"]]
)

Cool and warm colors

EK Ic, 1970

Finding palettes

What inspires you?

IKEA, 1984

Studio GdB

@hyunakimberly

Luna Wear

My Bloody Valentine

Hannah Stahl

Can Yang

IKEA, 1984

What inspires you?

When in doubt, steal

ColorGrab

Some concrete sources

Remix

  1. Open the file exercises/02-homage-to-the-square/exercise-2.Rmd
  2. Find an inspiration image and extract colors, or choose a palette from one of the sources above. Make sure you have at least 4 colors!
  3. Update the function make_my_homage() to take your palette as the default value of the colors argument
  4. Run the function make_my_homage() with a few different seeds and pick your output
  5. Share the inspiration and output in the GitHub discussion for this exercise
10:00

Inspired by Homage to the Square

Seven Basic Colors and All Their Combinations in a Square within a Square: Wall Drawing for Josef Albers (Wall Drawing 1176), Sol LeWitt

Reinterpet

  1. Take a few minutes to imagine what kind of work you could create inspired by Homage to the Square
  2. Use whatever tool you like - paper, ggplot2, figma, anything you’re comfortable with!
  3. Share your work in the GitHub discussion for this exercise
10:00

So far you’ve…

  • Learned how to recreate an Homage to the square using geom_rect()
  • Saved work in a good size/resolution using ggsave()
  • Created your first generative art system and using set.seed() to control randomness
  • Explored HSL as a color system, and learned how to alter colors using their saturation and lightness
  • Learned different methods of choosing cohesive color palettes color: monochromatic, analogous, complementary, triadic
  • Found color inspiration and added your personal color touches to the Homages
  • Imagined what other work you can create using rectangles and color