Week 2 – Computer Graphics and Art

1. Concept

In the beginning of this process in producing a computer graphic artwork, I was inspired by traditional patterned Indonesian batik. As I searched up some designs, my attention was kept by one particular design. I was struck by the magnitude of breathtaking blue and daring strokes of the artist here. I knew that for my artpiece, I wanted to incorporate water waves.

Batik by Tatang Wibowo, Javanese contemporary batik artist. Taken from https://www.batikguild.org.uk/batik/indonesia.

I was partly inspired by my memories of swirls I’d seen in Starry Night by Vincent Van Gogh and the almost swirly-shaped water wave Under the Wave off Kanagawa by Katsushika Hokusai. I wanted to incorporate patterns of swirls which highlight the tidal aspect of seawater, along with sine waves to depict its wavy nature.

Starry Night by Vincent Van Gogh. Taken from https://artsandculture.google.com/asset/bgEuwDxel93-Pg (Public Domain)
Under the Wave off Kanagawa (Kanagawa oki nami ura) by Katsushika Hokusai. Taken from https://www.metmuseum.org/art/collection/search/45434.

I thought of having my artwork depict a scene, so while keeping patterns as a motif in this piece, I added a sun composed of concentric circles, a sky made of turquoise and cyan waves (because I thought these colours looked cool) and rainfall. However, upon revising my plan much later, so that I would draw a night scene, I realised that a moon instead of a sun makes more sense, as well as dark blue and violet colours for the sky instead of turquoise and cyan.

Plan for Alisa’s Computer Graphic Artwork.

In order to code up this piece, I first had to study the fundamentals. To generate a spiral, I studied The Coding Train’s explanatory video on polar coordinates. It was very crucial in helping me understand why using the 2D polar coordinate system (location described in terms of radius and angle) is crucial in drawing special (and especially circular shapes) than the 2D Cartesian coordinate system (location described in terms of x- and y- coordinates).  Through this video, I got the idea to draw a spiral: I could use the formula for a circle, except I would increase the angle while decrease the radius over time. This is much preferred over using describing a multitude of precise x- and y- coordinates.

At first, my idea to generate an animated sine wave was to move an entire sine wave line to the left or to the right. However, after watching two of The Coding Train’s explanatory videos one on simple harmonic motion and one on graphing sine wave, I realized there’s another really interesting approach. Start from a dot oscillating up and down, then make an array of such dots, all oscillating up and down but with varying period and frequency.

2. Code Highlights

Firstly, generating the spirals, a complex shape is a highlight of the project, and a huge challenge as well. I faced multiple issues:

  • Incorrect loop structure: The inner loop used ‘i’ instead of ‘j’ as the variable, potentially causing a conflict with the outer loop. Additionally, the inner loop was never updated, potentially causing an infinite loop.
  • Spiral positioning: My translate() was not positioning each spiral correctly, resulting in spirals being drawn on top of each other.
  • Spiral reset: Whilst the angle and radius were reset, it was done outside the loop that drew the spiral, so this would cause just one dot of each spiral to be visible, with each point of the spiral being drawn on top of the other spiral points.

At one point, my canvas wasn’t visible, and I think it was because of an infinite loop occurring. AI was a useful resource to help me resolve this, as well as provide suggestions for my code. I learned one correct approach of drawing many spirals: by translating each spiral using i and j and incrementing by the spiral radius (in order to space out spirals evenly with some padding between them).

let angle_spiral = 2.5;
let r_spiral = 30;

function drawSpirals() {
  for (let i = 0; i < (height / 2 - 20) / (r_spiral + 20); i++) {
    for (let j = 0; j < width / (r_spiral + 20); j++) {
      push();
      translate(
        j * (r_spiral + 20) + r_spiral,
        300 + i * (r_spiral + 20) + r_spiral
      ); // spaces out spirals evenly with some padding between them
      strokeWeight(2);
      stroke(0, 191, 255);
      let tempAngle = angle_spiral;
      let tempRadius = r_spiral;
      while (tempRadius > 3) {
        let x = tempRadius * cos(tempAngle);
        let y = tempRadius * sin(tempAngle);
        point(x, y);
        tempAngle += 0.2;
        tempRadius -= 0.2;
      }
      pop();
    }
  }
}

 

Second, another highlight is the sine waves representing the sky. To implement the array of dots, we need to first find out the total number of wave dots that can fit across the entire width of the canvas. Then comes the mapping function, which becomes really important in generating the sine waves. In this case, it maps each point’s position i along the wave to an angle between 0 and 8*PI radius.

let sky_angles_wave = [];
let r_wave = 0.5;
function setup() {
  ...
  let total_wave_dots = floor(width / (r_wave * 2));
  ...
  for (let i = 0; i < total_wave_dots + 1; i++) {
    sky_angles_wave[i] = map(i, 0, total_wave_dots, 0, 8 * TWO_PI);
  }
  ...
}

In the drawing function, the x-coordinates (or angle) of the sine wave will be mapped to span the entire width of the canvas (600 px). The y-coordinates (or sine of each angle in sky_angles_wave) is mapped to a value between -15 to 15, resulting in a sine wave with an amplitude of 15.

function drawSky() {
  for (let y = 15; y <= 210; y += 30) {
    push();
    translate(300, y);
    fill(0);
    strokeWeight(6);
    stroke(81, 97, 199);
    beginShape();
    for (let i = 0; i < sky_angles_wave.length; i++) {
      let y = map(sin(sky_angles_wave[i]), -1, 1, -15, 15);
      strokeWeight(2);
      let x = map(i, 0, sky_angles_wave.length, -300, 300);
      vertex(x, y);
      sky_angles_wave[i] -= 0.001;
    }
    endShape();
    pop();
  }
  for (let y = 15; y <= 210; y += 30) {
    push();
    translate(300, y);
    noFill();
    strokeWeight(4);
    stroke(0, 104, 198);
    beginShape();
    for (let i = 0; i < sky_angles_wave.length; i++) {
      let y = map(sin(sky_angles_wave[i] + PI), -1, 1, -15, 15);
      strokeWeight(2);
      let x = map(i, 0, sky_angles_wave.length, -300, 300);
      vertex(x, y);
      sky_angles_wave[i] -= 0.001;
    }
    endShape();
    pop();
  }
}

Each point can shift down and up using: sky_angles_wave[i] -= 0.001;

Last but not least, apparently creating dotted lines (for the rainfall) is quite complicated. So I tried to think of another way. I used black sine waves to cover the grey rainfall.

All in all, this was a complex code!

3. Embedded sketch

4. Reflection and ideas for future work

I found this project to be very engaging, from the brainstorming stages to the coding process. I found the challenges a valuable opportunity to grow in learning how to graph complex shapes and animate lines (animation was something I wanted to learn from last week!). I have achieved the goal of creating a piece similar to the plan I intended, but have also exceeded that by adding in animations.

I hope the artpiece will be a calming one. An idea for the future is to animate the spirals to shift along with the deeper blue sine water waves. The moon could also be animated to grow and dim in brightness over time. Additionally, it would be cool to learn to animate rainfall (the technique used here was more of a special workaround, and I drew black lines over the moving water, the supposedly “invisible” black waves become more prominent!).

Reading Reflection – Week#2

Casey Reas’ Eyeo talk on chance operations highlights two important themes- chaos and order. He uses a mix of both chaos and order in many of his projects. It is very interesting when he uses chaos and order to visualize aspects of reality, such as biological data, specifically proteins communicating with a cancer cell through signals. A point I captured in this video is that adding a slight bit of noise keeps it more homeostatic and away from order – I understand it as a way to create a sense of randomness, or more objectively, pseudo-randomness. Tuning a group of objects with the same rules creates “behavior”  for that group. I am surprised to see how incorporating pseudorandomness into multiple groups with different behavioral rules adds a layer of complexity. I was struck at the consideration of observing the visualizations of the paths these objects take, rather than the placement of each object at a given moment; not only does this suggests I should consider art visualizations from multiple perspectives (not just position in multiple timeframes but velocity over time) but also acts as inspiration for a potential future project.

Casey Reas mentions that we can actually control how much we want to tailor or customize and leave to chance. This means we can decide the balance between total randomness and complete control, by setting quantitative figures on scalar, angle or colour randomness. As I considered where I feel is the optimum balance between total randomness and complete control, I was left with a question instead: is there an optimum balance between the two? I personally felt there is no wrong balance as long as tuning the two allows the artist to obtain the objective of the artwork.

I would like to incorporate randomness in my work specifically through incorporating randomness in the scaling of spirals, as well as amount of tilt. Beyond the visual arts, this video did raise a surprising idea of experimental music by random chance operations, such as in duration or pitch, which could be a great idea to try out in the future.

Assignment 1 – Self-Portrait

1. Concept

I started by brainstorming ideas about some things I like to give my self-portrait some fun personality. I thought about netball, but wasn’t sure about the other things to add yet.

Then I tried making a sketch on my tablet to plan out what I wanted my piece to look like. I understood that the planning process is really important because it will save time in the long run as I have a direction to stick to. A big consideration as I decided how to draw the different objects in the sketch was the types of shapes or lines I could use in p5 js. Starting with my face, I drew a circle for the head, arcs for my hair outline, arcs for both outlines of my eyes, circles for my eyes, as well as arcs for my eyebrows, nose and lips. I liked my red sweater, which had a high neckline, so I added a line around my neck.

I tried thinking more about what I like, and with the help of online sources, I realized pets and travelling are two of them! I like dogs, so I wanted to draw a simple dog. I could use ellipses for the eyes and ears, triangle for the nose, arcs for the mouth. I would need a large ellipse for the dog’s body, as well as ellipses for the dog’s legs. For the tail, I could make an arc. A good way to show I like travelling is through trains. I like mountains, especially thorugh my experience travelling and hiking in Wales. Specifically, snowy mountains are special for me. At first, the train was a small icon to the right of me, but then with some inspiration from Dev Kalavadiya’s Self Portrait, I realized the mountains could be in the background. So I changed the structure of the work, placing the mountains to the top-left, the netball and pole to the right. I thought it would be cool to have the train be directed toward the mountains.

2. Challenges with Code Highlight

It’s my first week using p5 js and we got practice in-class to make shapes and lines, but I’m unfamiliar with animation. Thus, I expected a challenge with the animating, particularly in the snowfall. But I thought it will be fun to try.

I also realized that my nose is more appropriately drawn with a spline curve rather than arc(s). I faced trouble making the spline curve, however, with it being too small, but was able to resolve it with help of AI.
I realised that if I want my hair to be coloured in, it is simpler to use a shape and fill it in rather than use arcs and fill in what’s inside. I decided to use ellipses and rotate them as needed. However, my attempt didn’t work:
// Top right hair outline
angleMode(DEGREES);
let right_hair = ellipse(width / 2 + 40, height/2, 100, 50);
right_hair.rotate(45);

Turns out that ellipse() doesn’t return an object I can rotate. Instead, I need to use p5.js transformation functions. It’s important to push() and pop() so that rotations don’t accumulate in case I rotate other shapes:

 // Top right hair 
angleMode(DEGREES);
push(); // Save the current transformation state
translate(width / 2 + 40, height / 2); // Move to where we want to rotate around
rotate(38); // Rotate by 38 degrees
fill(55, 22, 13);
noStroke();
ellipse(0, 0, 100, 50); // Draw at (0,0) since we've translated
pop(); // Restore the transformation state

After shifting around my code so that the neck and body comes after the top back hair but before the eyes, I was shocked to find out that the eye outlines somehow disappeared. I tried experimenting with removing and putting back code to find out what the issue was. Since the code for neck was relatively simple, I realized the issue must be with the body. I had an idea to use push() and pop() just like for the hair, and phew, it worked!

To make the mountains, I printed mouseX and mouseY in draw() function, hovered over the points I wanted the vertices of the triangles to be in in order to get the approximate coordinates of the triangle vertices for triangle().

Making the train tracks was especially time-consuming, as I was needed to ensure I drew the horizontal train tracks using quadrilaterals progressively bigger as it goes more downward on the page. I had to consider and ensure the spacing between the train tracks were reasonable, and I used maths calculations to help me with this. Phew, after about 30 minutes working on this, I was done.

Trying to create the netball allowed me to experiment with arcs, getting more familiar with what each parameter is used for.

3. Embedded Sketch

4. Reflection and Ideas for Future Improvement

Due to the time constraint, I decided not to draw the dog; nevertheless, my main goal to draw a self-portrait and things I like are completed. Through this experience, I learned more on layering, p5.js transformation functions, special lines extending beyond the class material, such as spline curves and bezier curves. A challenge I faced personally was being patient especially while drawing the train tracks, so this experience was a valuable opportunity to develop my resilience.
Future ideas for improvement would be adding in the animations, especially snowfall and the train moving, as well as adding interactivity such as buttons for the user to click.