New Project! R-based Art

Inspiration

The late great Parisian chef Auguste Gusteau (who rose to international fame in 2007 upon the release of his son Alfredo Linguini’s biopic) built a culinary empire on the simple phrase: Anyone can cook. According to Gusteau, an individual’s potential to create art only requires them to be able to appreciate it. The only difference between a consumer and a creator lies in their learned skill with the brush. Should he have a good nose for beauty — in theory — even a rat could cook.

As long as you know what you like, you should be capable of creating art that makes you happy. Figured I’d try my hand at making something that looked nice

Medium

After a brief foray into pencil and paper I decided to get back to my roots — R code. With some work I certainly could’ve learned a different method (classical physical art techniques, digital coding libraries) but in the interest of wanting to jump in and create I opted for the tool I spend the most time using. R is traditionally a statistical coding platform used primarily by academics and social scientist but fundamentally has most of the same capabilities as Python.

Research

R has a bit of a reputation for being a ‘soft’ or ‘weak’ programming language that I think in part comes from an association with the people who use it (most R coders I’ve met lack software development training). As a result, I was unable to find many records of people using this technology for visual art. Danielle Navarro’s Art From Code blog is the foremost resource on the subject so I started by working through a few of their tutorials in order to get a feel for the medium. Ultimately I decided to build my own framework.

Backend

Navarro works through applications of several modes of the ggplot graphing library, but my takeaway from this exercise was that each mode has distinct limitations that would eventually constrain my ability to create. For example plotting using ‘geom_point’ only allows for plotting dots, or dot-like objects, and ‘geom_line’ would struggle when asked to do run complex line width functions that varied over time.

Starting with the most barebones ggplot display mode — geom_raster — I built a library from scratch.

Given an ‘x’ and ‘y’ dimension, we can generate a 3 column ‘canvas’ dataframe with x times y rows where column 1 is every x coordinate, column 2 is for y coords, and column 3 is some real number value that we call ‘paint’. In this way, we have total control over every pixel in an image. Then we can write functions that modify this canvas.

Below is an example of our simplest function: ‘add_line’

Given a canvas, two x-y coordinates, and a numeric value, this function ‘draws’ a line of a given thickness on the canvas by setting the paint value of all the rows with x-y coordinates within the line region equal to the value.

For very large canvases (I’ve wound up utilizing canvases with upwards of 30 million rows), this can end up being slow. A couple optimization notes:

  • The function runs by checking if each coordinate is within the rectangle forming the line. We save a lot of time here by only checking coords within the box bounded by the start and endpoint of the line instead of all coords in the canvas. This means that long diagonal lines are still slow but short lines are very fast.

  • Classically, looking up a coordinate in an array like this (for the purpose of checking its paint value or coloring it) would be done by scanning through every row in the array and seeing if the x and y values match. This means millions of operations in each lookup. We can get around this by using the unique construction of our canvas. By writing the function that creates the canvas, we know exactly what row in a canvas a given x-y coordinate will be found at. R is very fast at looking up the nth row in an array if it doesn’t have to look up any others.

We more or less follow this process for a host of different shapes: circles, arcs, flower petals, grids, comets, parametric curves, etc.

One other critical function to this library is the ‘mask’ function. Sort of like the Photoshop magic wand tool, calling the ‘mask’ function on a canvas with a given x-y coord pulls all the coordinates in the contiguous convex region around that point that have the same paint value. I want to expand this capability to concave regions but doing so sacrifices a ton of efficiency.

Creations!

This raw backend comes out to just about 2000 lines of code and once that’s written we can build! I’ll be making more posts in the future about design but I’ll start by sharing a couple of my first creations:


Petals are drawn by creating parametric curves with a custom function, masking and coloring the interior. Random elements here are added noise in paint values and ordering of petal layering. Runtime for 18M rows: 2 minutes

Super simple bunch of circles. Instead of assigning paint values to 1 wherever a circle is drawn I tried adding 1 to the existing paint value to highlight interplay. I kinda like it. Good colors go a long way. Runtime for 9M rows: 3 minutes



More to come!

Next
Next

Why do I hate spicy ramen so much??