Time Flowing By (Abstract Clock)

Processing Sketch

Code

Main

// Credit to Golan Levin for time boilerplate and Daniel Shiffman for Sine Wave Example

int prevSecond; 
int lastRolloverTime; 
int mils;

int h;
int m;
int s;

Wave secondsWave;
ArrayList minutesWaves;
ArrayList hoursWaves;
float baseWaveHeight;

void setup() {
  size(640, 360);
  baseWaveHeight = height/8;

  stroke(120);
  setupGlobalTimes();

  // initialize objects
  hoursWaves = createHoursWaves();
  minutesWaves = createMinutesWaves();
  secondsWave = createSecondsWave();

  lastRolloverTime = 0; 
}

void draw() {
  background(255); 

  setMils();
  setupGlobalTimes();
  // uncommen the line below to draw time as text for debugging
  // drawTime();

  pushMatrix();
  // int hShift = mils();
  translate(0, height/2); // work relative to horizontal venter at middle

  float periodInSeconds = 60.0; 
  float periodInMilliseconds = periodInSeconds * 1000.0; 
  float timeBasedSinusoidallyVaryingQuantity = sin(TWO_PI * millis()/periodInMilliseconds);

  /* calculate and update seconds waves */
  float secondsVShift = map(timeBasedSinusoidallyVaryingQuantity, -1, 1, height/2, (height/3));
  secondsWave.verticalShift = secondsVShift;

  float millisToCrossScreen = 1000.0;
  float secondsHShift = getCurrentHShift(millisToCrossScreen);

  secondsWave.horizontalShift = secondsHShift;
  secondsWave.update();

  if (m != 0) {
    Wave bottomMinutesWave = minutesWaves.get(0);
    float mergeDegree = millis()/periodInMilliseconds;
    secondsWave.mergeWaves(bottomMinutesWave, mergeDegree);
  }

  strokeWeight(2);
  secondsWave.display();

  /* calculate and update minutes wave */
  millisToCrossScreen = 3 * 1000.0;
  int minutesPerWave = 5;
  for (int i = 0; i < minutesWaves.size(); i+=minutesPerWave) {
    Wave minutesWave = minutesWaves.get(i);
    float minutesHShift = getCurrentHShift(millisToCrossScreen);
    minutesWave.horizontalShift = minutesHShift;
    minutesWave.update();
    strokeWeight(1.5);
    minutesWave.display();
  }

  /* calculate and update hours wave */ 
  millisToCrossScreen = 30.0 * 1000.0;
  float hoursHShift = getCurrentHShift(millisToCrossScreen);
  for (int i = 0; i < hoursWaves.size(); i++) {
    Wave hWave = hoursWaves.get(i);
    // hWave.horizontalShift = hoursHShift*(i/3);
    hWave.update();
    strokeWeight(1);
    hWave.display();
  }  
  popMatrix();
}

float getCurrentHShift(float millisToCrossScreen) {
  float hShift = millis()/millisToCrossScreen;
  return hShift;
}

Wave createSecondsWave() {
  float amp = baseWaveHeight*1.1;
  float freq = 0.75;
  // arguments for Wave constructor are (amplitude, frequency, horizontalShift, verticalShift, pointSpacing)
  Wave sWave = new Wave(amp, freq, 0, 0, 5);
  return sWave;
}

ArrayList createMinutesWaves() {
  ArrayList minutesWaves= new ArrayList();
  println("m " + m);

  if (m != 0) {
    for (int i=0; i < m; i++) {
      println("i " + i);
      float amplitude = baseWaveHeight/3;
      float frequency = 1.5;
      int pointSpacing = 5;
      float allShift = 1*(height/10);
      float perWaveOffset = i*(baseWaveHeight/30);
      float vShift = allShift + perWaveOffset;
      float hShift = i*(width/20);
      Wave mWave = new Wave(amplitude, frequency, hShift, vShift, pointSpacing);
      minutesWaves.add(mWave);
    }
  }
  return minutesWaves;
}

ArrayList createHoursWaves() {
  ArrayList hoursWaves= new ArrayList();
  // for every hour but the current one, draw a very slow moving wave at the top of the screen
  if (h != 0) {
    for (int i = 0; i < h; i++) {       // arguments for Wave constructor are (amplitude, frequency, horizontalShift, verticalShift, pointSpacing)       float amplitude = baseWaveHeight/4;       float frequency = 1;       float perWaveOffset = (i*(baseWaveHeight/4));       float allShiftUp = -1*(height/2);        float vShift = allShiftUp + perWaveOffset;       int pointSpacing = 5;       int hShift = 0; // (i/3)       Wave hWave = new Wave(amplitude, frequency, hShift, vShift, pointSpacing);       hoursWaves.add(hWave);       }   }   return hoursWaves; } void setupGlobalTimes() {   // Fetch the components of the time (hours, minutes, seconds, milliseconds).   // Incidentally, you can also get day(), month(), year(), etc.    h = 15;//hour();    m = 7;///minute();    s = 30;// second();  } void setMils(){   // The millis() are not synchronized with the clock time.    // Instead, the millis() represent the time since the program started. Grrr.    // To approximate the "clock millis", we have to check when the seconds roll over.    if (s != prevSecond){      lastRolloverTime = millis();    }   mils = millis() - lastRolloverTime;   prevSecond = s; } // just here to help develop and debug void drawTime() {   setMils();   //-------------------------------------------------   // Assemble a string to display the time conventionally.   String hourStr   = nf(((h > 12)? h-12:h), 2); // format String with 2 digits
  String minuteStr = nf(m, 2); // format String with 2 digits
  String secondStr = nf(s, 2); // format String with 2 digits
  String ampmStr   = (h < 12) ? "AM" : "PM"; 
  String milsStr   = nf(mils, 3); 
  String theTimeString = hourStr + ":" + minuteStr + ":" + secondStr; 
  theTimeString += "." + milsStr + " " + ampmStr; 

  fill (0); 
  text (theTimeString, width/5, 10); 
  noFill();
}

Wave Class

class Wave {
  float amplitude;
  float frequency;
  float horizontalShift;
  float verticalShift;
  int pointSpacing;
  ArrayList points;

  Wave(float thisAmplitude, float thisFrequency, float thisHorizontalShift, float thisVerticalShift, int thisPointSpacing) {
   amplitude = thisAmplitude;
   frequency = thisFrequency;
   horizontalShift = thisHorizontalShift;
   verticalShift = thisVerticalShift;
   pointSpacing = thisPointSpacing;
   points = new ArrayList();
  }

  // gets the y value for a given x position on this wave
  float getY(float xAngle) {
    float y = amplitude * sin((frequency * xAngle) - horizontalShift) + verticalShift;
    return y;
  }

  // calculate and store positions for each point that's a part of this wave
  void update() {
    points.clear();
    // for each x position across the window, get the y outputted by the wave function
    for (int x=0; x < width; x++) {
      float xAngle = map(x, 0, width, 0, TWO_PI);
      PVector xyPoint = new PVector(x, getY(xAngle));
      points.add(xyPoint);
    }
  }

  // take this wave and make it gradually more similar with another wave
  void mergeWaves(Wave anotherWave, float degree) {
    ArrayList newPoints = new ArrayList();
    // average points along this wave and the other wave's path to the degree
    // passed into the function
    for (int i = 0; i < points.size(); i++) {
      if (i < anotherWave.points.size()) {
        PVector thisPoint = points.get(i);
        PVector thatPoint = anotherWave.points.get(i);
        float mergeX = lerp(thisPoint.x, thatPoint.x, degree);
        float mergeY = lerp(thisPoint.y, thatPoint.y, degree);
        PVector mergePoint = new PVector(mergeX, mergeY);
        newPoints.add(mergePoint);
      }
    }
    points = newPoints;
  }

  void display() {
    // setup drawing settings
    smooth();
    noFill();
    // println("points.size() = " + points.size()); 

    // draw points that make up the curve
    beginShape(LINES);
    for (int pointNum = 0; pointNum < points.size(); pointNum++) {
      PVector point = points.get(pointNum);
      vertex(point.x, point.y);
    }
    endShape();
  }
}

Most up-to-date code: https://github.com/juliat/abstract-clock

Reflection

For this project I had two things in mind. First, I wanted to create a clock that I’d actually want to look at, something that I could have on my desk that would give me a sense of the time and also be pleasurable to look at every once in a while. Second, I wanted to create a clock that would say something about how time feels. For me, that meant creating a clock with waves flowing at different paces. Seconds pass quickly, like waves on the sand, while hours are more like the waves you see as you look at the horizon—almost solid, stately.

I like how this sketch has come out so far—the overall feel is going in the direction that I want it to. Still, there are many things I would like to improve and polish about it. For example:

  • Making the waves fade more smoothly into one another (a better version of what the seconds wave does with the minutes wave right now)
  • Explore readability of the clock—making it easier to actually tell the time using the clock.
  • Giving more attention to the transitions between hours and minutes over the course of a day. Right now, waves just appear or disappear suddenly. They should really fade or merge into one another or something.

All/Other Ideas for This Project

1. Water and Fishes

Abstract Clock Sketches Page 1

2. Abstract Waves, 3. Circles

Abstract Clock Sketch Page 2

Waves – Texture and Pattern Research

Circles
Color/circle grid inspiration.

4. Slices of Sun and Moon

Abstract Clock Sketches

 

Comments are closed.