Making Sol LeWitt’s colored bands interactive with d3.js

by admin

Skip to the full page interactive version if you don’t want to read!

I made a post a while ago about recreating a Sol LeWitt piece using d3.js, and making it interactive. I had a lot of fun doing that one, and it wasn’t even my favorite of his stuff that I saw! So, I knew I’d be back.

In some of his exhibits, he uses color in really great ways. The one I recreated previously didn’t have a lot of color, so I wanted to do a really colorful one this time. It seemed like of the ones at MASS MoCA, the colorful ones could be divided into two categories: either really bold, primary and second colors, like:

or, ones with more faded, more “complex” colors, like:

The one I’m doing today falls into the second category. Here are a few pics of it:

The basics of building it weren’t hard, but the devil’s always in the details. I created the basic structure using a d3 stack shape, and used GIMP to figure out the colors he used. Practically, this meant defining the “bottom” shape (a sine wave), and then adding layers on top of it; specifically, you choose the equation of the bottom layer, and then specify the thicknesses of the layers on top of it. However, if you simply do this with a bottom sine wave layer and constant thicknesses, you’ll get a pretty boring result:

function sine_bottom_stack(n, m){

  var curves_stack = [];
  var pi = 3.14;
  var tau = 2*3.14;
  var f_0 = 1.0/m;
  var sine = [];

  // BOTTOM LAYER
  for (let j = 0; j < m; ++j){
    var A = 4;
    var f_mult = 2.5
    sine[j] = A*Math.sin(phase_0_sine + tau*f_0*j*f_mult);
  }
  curves_stack[0] = sine;

  // LAYER THICKNESSES
  for (let i = 1; i < n; i++){
    const a = [];
    var A = 0.6*t_band[i];
    for (let j = 0; j < m; ++j) {
      a[j] = 1.5
    }

    curves_stack[i] = a;
  }
  return curves_stack;
}

(n is the number of layers, m is the number of points used to define each curve. I do f_0 = 1.0/m to make the frequency you actually see on the screen be independent of the number of points you use to make the curve.)

Of course, there’s a lot more going on in the real thing. The first thing is that it’s obviously not a simple sine wave, it has some pretty funky variation.¬† For the sake of not having this be a huge wall of mostly repetitive code, I’ll just use latex from here on out. x is the position that the curve is determined by, b is the equation for the base sine layer, a_i will be the thickness of curve i, and I’ll define other things on the way. So, what I have above is simply:

f_{mult} = 2.5  b = 2 \textrm{sin}(2\pi f_0 f_{mult} x)  a_i = 1

The first thing is that the bands are obviously different thicknesses. To to this, I just made each a_i a random term:

t_i = 1 + 1.2 \mathcal{U} (0, 1)  a_i = t_i

where \mathcal{U} (0, 1) is a uniform distribution. I’ll leave the b term as it is above for now, as we can get most of the interesting behavior by changing the a_i terms. I’m including the t_i here for reasons you’ll see shortly. This gives:

Slightly better. The next pretty obvious thing is that they’re not regular sine waves, they have variations across the length. To do that, I made a_i have spatial dependence:

t_i = 1 + 1.2 \mathcal{U} (0, 1)  f_{band} = 2  a_i = t_i (1 + 0.6 \textrm{sin}(2\pi f_0 f_{band} x) )

Looking better! This is definitely the “Pareto jump” of this problem. However, it’s still not quite there. The base thicknesses of each layer (t_i) are all different, and now each thickness is being modulated by position, which looks better, but they’re still being modified by position¬†by the same amount and in the same places. One easy way to change this is to add a phase term, \phi_i, that’s constant for each band, but selected randomly for each:

t_i = 1 + 1.2 \mathcal{U} (0, 1)  \phi_i = 2\pi \mathcal{U} (0, 1)  f_{band} = 2  a_i = t_i (1 + 0.6 \textrm{sin}(2\pi f_0 f_{band} x + \phi_i) )

Hot daaaamn! We’re getting there. That has the effect of making the modulation to a_i just shift for each band, but it’s still actually the same amount and happening at the same rate. Alternatively, we can do a similar idea, but for f_{band} instead:

t_i = 1 + 1.2 \mathcal{U} (0, 1)  f_{band_i} = 1 + \mathcal{U} (0, 1)  a_i = t_i (1 + 0.6 \textrm{sin}(2\pi f_0 f_{band_i} x) )

DANG. In my opinion, that looks even better. And, together:

t_i = 1 + 1.2 \mathcal{U} (0, 1)  \phi_i = 2\pi \mathcal{U} (0, 1)  f_{band_i} = 1 + \mathcal{U} (0, 1)  a_i = t_i (1 + 0.6 \textrm{sin}(2\pi f_0 f_{band_i} x + \phi_i) )

Now we’re cookin.

At this point, the following were basically a bunch of little tweaks I did by hand, to taste. To make a long story short, at this point I adjust the base sine again. I make it have a larger amplitude, a few randomly sampled phases, an amplitude that varies with position, and a frequency that varies with position:

\phi_b = 2\pi \mathcal{U} (0, 1)  \phi_0 = 2\pi \mathcal{U} (0, 1)  \phi_f = 2\pi \mathcal{U} (0, 1)  A = 4 (1 + 0.3 \textrm{sin}(\phi_b + 2\pi f_0 x) )  f_b = 2.5 (1 + 0.2 \textrm{sin}(\phi_f + 2\pi f_0 x) )  b = A \textrm{sin}(\phi_0 + 2\pi f_0 f_b x)

Note that this has the effect of making b have a nested sine!

Pretty close to the main idea in my opinion!

However, this is just one part. To make mine a little more fun, I wanted to have it be time varying. To do this, I made it so it periodically updates a few of the variables that should give some shape change, especially in aggregate. Each time step, it does:

C = 1  \phi_0 \rightarrow \phi_0 + 0.02 C  \phi_b \rightarrow \phi_b - 0.04 C  t_i \rightarrow t_i + 0.02 C \mathcal{U} (-0.5, 0.5)  f_{band_i} \rightarrow f_{band_i} + 0.005 C \mathcal{U} (-0.5, 0.5)

I also wanted to make it so the user could control how much it changes, but not have them mess with actual numbers. To do this, I have it capture the current mouse position. There’s a “band” in the middle of the window where, if the cursor is within that band, it won’t change at all. Outside that band, it changes faster depending on how far outside of it you are (to the maximum at the edge of the screen). That’s why that C is up there! It’s dependent on the mouse position. I’m curious if people will figure that out or not?

Another interesting aspect to this is that, due to how I’m updating t_i and f_{band_i} above, they’ll act as random walkers: having average 0 change in value over infinity, but large fluctuations too.

There’s one last aspect to this, and it was actually a doozy and pretty interesting. If you notice, in his painting, they’re not solid colors, and it gives this really interesting “mottled” effect. My friend Bobby said that he read one of the plaques at the museum and it talked about the process, something known as “India washing” where they paint in layers, doing things to them in between. I really wanted this effect, but wasn’t sure how to do it. I thought d3.js might have some clever way of doing it, but I couldn’t find it despite my best Google-fu. I knew that what I wanted had to involve some way of randomly changing the intensity of the color, with some specified correlation distance (so it would have that “patchy” look and not just be seen as a uniform dimmer color).

I finally found a way, using a technique I didn’t know about before: SVG filters. They’re really interesting and I’ll definitely be using them in the future, because they seem really powerful. Here, I’m using the feTurbulence filter, which uses Perlin noise, to create the mottled look. By itself, it looks like this:

To use this, I had to employ a couple other filters. Filters are kind of functional in the sense that you can “chain” them together to make more complicated effects. First, I applied a grayscale to the above one. Then, I used the composite filter in xor mode to combine it with my svg of the bands. This makes it so where the noise filter is lighter, the colors will appear more, and vice versa, giving the mottled look:

Ohhhhhhhhhhh yes.

One downside is that it’s applying the same filter to the whole svg, so if you look closely, you can see when it moves that there’s a kind of constant pattern “behind” the bands (or if you look closely when it’s stationary, you can see some patches cross band boundaries). Ideally, I’d like them to each have their own noise pattern. I can do this, but when I tried instead applying the filter to each band, it was incredibly slow and basically couldn’t move. I’m not exactly sure why this is. On the one hand, I know why it might be slow: it’s just doing a pixelwise xor each time, which has gotta be intensive. On the other hand, it apparently does it fine for the whole svg. When I do it for each band, it’s only doing it for the bounding area of the band. Maybe I’ll figure it out someday, but for now it’s a pretty diminishing return.

Anyway, here’s the full, interactive version! Give it a try. If you like how it looks at a given point, there’s a button to save the image! (without the button itself in the image :P)

It was tons of fun to make this and I’m sure I’ll do it again in the future. d3.js is very cool and Sol LeWitt’s works are perfect for it. See you next time!

And others

2 comments

Deborah September 17, 2019 - 9:16 am

Impressive as always (though frankly, as an artist the implications are a bit creepy, but never mind…). I’m delighted by this exploration of yours.
To be accurate, the ‘first type’ example you show has secondary and tertiary colors with some shades and tints of same, while the second is tertiaries and secondaries with lots of scumbling to achieve multiple shades and modulation of forms. The way the lopped-off pyramid seems to float is remarkable, isn’t it?
I think the boldness in the first type is due to value contrasts more than specific kinds of color.
Do we know how much of these pieces were done by LeWitt himself and how much by his usual crew, from his instructions? Yes, probably a googlable question…

Reply
admin September 19, 2019 - 3:02 pm

I know that in most museums, LeWitt didn’t do it himself! Though I’m not sure if the artists that did were chosen by him or not. I forget where, but I remember reading that at one the museums with his stuff, there was some confusion about who actually made the piece (they thought it was him but it wasn’t maybe?).

Reply

Leave a Comment