Celine Nguyen

21 Jan 2014

Process

In my sketches, I started off with things transforming to other things or some kind of narrative:

Early sketches

I realized that a step-by-step/progressional/narrative-based animation would be difficult to get to loop (especially given our frame limitations). So I spent some time looking at other GIFs (you can see some of my favorites here) and that influenced my decision to do something less concrete and more abstractly geometric.

I really wanted to create something that looped smoothly, and I wanted to figure out how to achieve natural-looking motions. I explored in my sketches some ideas involving a lightbulb and circle shapes, so I decided to combine those ideas and do something a bit more abstract.

I’ve been drawing a lot of lighting fixtures lately, so the circular fan of lines grew from the idea of a chandelier.

 

Result

I went a little crazy with the idea of animating things with a sin function and ended up trying to animate the length of the lines (my initial visual device) but also the background color (to show space being lit up) and the opacity of the lines with the tiny end bulbs (slowly turning on?).

Along the way, I figured out some things I’d definitely like to learn more about:

  • Techniques for smooth color transitions. The background color changes in little jumpy motions.
  • A rate of change for elements that feels more dynamic/variable. I think here the motion of the lines is a bit too regular to feel like an active object, given what I’m trying to do with the changing color.
  • How to have elements smoothly change in proportion to one another. The same sets of lines are the longest/shortest/middle for the entire animation. It would have been interesting to have the sets change in length order (e.g. so the longest lines become the shortest).
  • Trig (and other mathematical bits for cooler transformations and motions). Forgot a ton of it (and also am unfamiliar with a lot of graphical applications).

This was a very useful exercise in terms of orienting myself with Processing and making some forays into motion and color, but I feel the GIF I produced was directed & constrained more by the things I didn’t know and wanted to explore technically. The concept is not as tight as the sketches I drew (and then abandoned). I think it would have benefited from some more consideration of making the motion (of the lines going in and out)  and color change create some sense of a specific, familiar object. It could have been a bit more lightbulb-y or chandelier-y.

Code

int     nFramesInLoop = 10;
int     nElapsedFrames;
boolean bRecording; 
String  myName = "celinenguyen";
boolean debug = false;
int     canvasSize = 500; // 500/1500
int     strokeSize = 2; // 2, 6
int     ellipseSize = 6; // 6, 18

float[] quadrantLength = new float[3];

void setup() {
  size (canvasSize, canvasSize); 
  bRecording = false;
  nElapsedFrames = 0;
  frameRate (nFramesInLoop);
}

void keyPressed() { 
  bRecording = true;
  nElapsedFrames = 0;
}

//===================================================
void draw() {
  // compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } 
  else {
    float modFrame = (float) (frameCount % nFramesInLoop);
    percentCompleteFraction = modFrame / (float)nFramesInLoop;
  }
  // render the design based on that percentage. 
  renderMyDesign (percentCompleteFraction);
  // if we're recording, save the frame to a file. 
  if (bRecording) {
    saveFrame("output/"+ myName + "-loop-" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames == nFramesInLoop) {
      bRecording = false;
    }
  }
}

//===================================================
void renderMyDesign (float percent) {
  float changingOpacity = sin(percent * (PI * 2)) * 255;
  quadrantLength[0] = 350 * (1 * sin(percent - 0.5)); // 350/1200
  quadrantLength[1] = 300 * (1 * sin(percent - 0.5)); // 300/1100
  quadrantLength[2] = 200 * (1 * sin(percent - 0.5)); // 200/800

  background(#1D1921); // dark purple
  smooth(); 
  strokeWeight (0);
  fill(#1F2638, changingOpacity);
  rect(0, 0, canvasSize, canvasSize);

  if (debug) {
    // displaying percentage
    fill(255);
    String percentDisplayString = nf(percent, 1, 3);
    text (percentDisplayString, 50, 50);
  }

  translate (canvasSize / 2, canvasSize / 2);
  color[] palette = new color[3];
  palette[0] = #E6E3BA;
  palette[1] = #ABA86A;
  palette[2] = #9C9628;
  int paletteIndex = 0;

  float length, x, y;
  fill(255, changingOpacity);

  int quadrantCount = 0;
  for (float i = 0.0; i < (2 * PI); i = i + (PI / 2)) {
    stroke(palette[0], changingOpacity);
    length = quadrantLength[0];
    strokeWeight (strokeSize);
    x = length * cos(i);
    y = length * sin(i);
    line (0, 0, x, y);
    strokeWeight (0);
    ellipse(x, y, ellipseSize, ellipseSize);
  } 
  for (float i = (PI / 4); i < (2 * PI); i = i + (PI / 2)) {
    stroke(palette[1], changingOpacity);
    length = quadrantLength[1];
    strokeWeight (strokeSize);
    x = length * cos(i);
    y = length * sin(i);
    line (0, 0, x, y);
    strokeWeight (0);
    ellipse(x, y, ellipseSize, ellipseSize);
  } 
  for (float i = (PI / 8); i < (2 * PI); i = i + ((2 * PI) / 8)) {
    stroke(palette[2], changingOpacity);
    length = quadrantLength[2];
    strokeWeight (strokeSize);
    x = length * cos(i);
    y = length * sin(i);
    line (0, 0, x, y);
    strokeWeight (strokeSize);
    ellipse(x, y, ellipseSize, ellipseSize);
  } 
}