Week 2 – Maneuvring Around Difficulties

Intro

Sometimes, unexpected pop-ups may not be serendipities but discoveries of limits that propel us to set off on a new adventure.

My expenditure this time didn’t turn out to be very smooth in terms of my realizing my initial vision and how that vision deviates from my expectations. On the other hand, I believe these sorts of experiences would be quite beneficial as a lesson learned to guide future planning.

As my initial vision (or product concept) changed many times in the course, it’s rather difficult to start this documentary with a definitive concept aforesaid. Therefore, I will briefly touch on the concept of each of the prototypes alongside my process description.

process

A Definitive Heart in 3D Space

In the first stage of my development, my goal was to construct a framework of basic interactions that could satisfy my needs later on – which was attempted later on but eventually did not stand for my final product. Hence, I stuck to the idea of generating a visible coordinate system in 3D space at the beginning and wrote a basic ‘arrow generating-shooting’ mechanism.

The loops in this prototype are mostly for displaying every ‘arrow’ I stored in an array and the axis plane in each frame.

let arrowArray = [];

function draw() {
...
// Display all arrows in arrowArray for 1 frame
  if (arrowArray[0]) {
    for (let i = 1; j = arrowArray[i] ;i += 1) {
      if (j._3Dz < -50) {
        arrowArray.splice(i, 1); // Remove the arrow that is behind the plane
      } else {
        j.arrowDisplay(); // A method in Arrow class
      }
    }
  }
}

// Draw axis plane on a Graphics object
function drawAxis() {
  // translate to 2D coordinate system
  axis.translate(-axis.width / 2, -axis.height / 2);
  
  axis.background(10);
  
  // draw the axises
  axis.stroke('white');
  axis.line(0, axis.height / 2, axis.width, axis.height / 2);
  axis.line(axis.width / 2, 0, axis.width / 2, axis.height);
  
  // draw the marks on the y axis
  for (let i = 0; i < width; i += axis.height / 20) {
    axis.line(axis.width / 2, 
         0 + i, 
         axis.width / 2 + 5, 
         0 + i);
  }
  
  // draw the marks on the x axis
  for (let i = 0; i < width; i += axis.width / 20) {
    axis.line(0 + i, 
         axis.height / 2,
         0 + i,
         axis.height / 2 - 5);
  }
}

Here, I adopted a math function that has the graphic appearance of a heart to test out the prototype. As the heart function could have multiple y values for one x value, I used the unit circle to generate the coordinates of the points on the function.

// Heart shape function
function functionHeartX(t) {
  return 160 * pow(sin(t), 3);
}

function functionHeartY(t) {
  return 150 * cos(t) - 55 * cos(2 * t) - 30 * cos(3 * t) - 10 * cos(4 * t);
}

Besides, I also tried some basic lighting to hue the ‘arrows’ with redish-pinky color. It was quite interesting to see how the gamma factor affects the actual light color.

lights();
ambientLight(50); // as a gamma factor
pointLight(
  255, 0, 0, // color
  0, 0, 0 // position
);

A Probability Heart in 2D Space

Then, I started a new prototype to specifically realize generating random positions based on the probability distribution in a 2D space (in order to later adopt into the 3D space). Multiple probability functions were tested in this stage, including:

// Gaussian probability function
function gaussianProbability(distance) {
  if (distance > maxDistance) {
    return 0; // No points beyond maxDistance
  }
  return exp(-pow(distance, 2) / (2 * pow(sigma, 2))); // Gaussian decay
}

// Quadratic probability function
function quadraticProbability(distance) {
  if (distance > maxDistance) {
    return 0; // No points beyond maxDistance
  }
  return max(0, 1 - pow(distance / maxDistance, 2)); // Quadratic decay
}

It is also noticeable that the default random() function could be used as such to mimic the probability realization:

// Add point based on probability
 if (random() < probability) {
   points.push(createVector(x + width / 2, -y + height / 2)); // Store the point
}

A Probability Heart in 3D Space

After that, it was much easier to adopt the probability parts into the 3D version (and to be presented as a cliche gift in a long distance relationship):

A Grayscale Webcam Downgrader

# Please access the p5js page directly from the instance to allow webcam/mic usage for the following demonstration.

On top of that, I started to experiment with the webcam as an input:

// Create a video capture from the webcam
video = createCapture(VIDEO, { flipped:true });
video.hide(); // Hide the default video element that appears under the canvas

My intentions in this stage is: 1. convert the video into grayscale values instead of RGBs (as the grayscale values can be later on mapped into probabilities); 2. downgrade the video resolution into a mosaic (as, for probability generation purposes, it would save a lot of time and be even more beneficial for the depiction of the overall shape that I planned to deliver with a collective of ‘arrows’).

To achieve the first goal, we have to operate directly on the pixel array that holds the RGB values for each pixel of each frame in the video with a time complexity of O(n) (instead of calling a filter() method for the displayed effect on the video stream when showing the video).

function videoToGray() {
  // Load the pixels from the video
  video.loadPixels();

  // Check if the video has pixels loaded
  if (video.pixels.length > 0) {
    // Convert to grayscale
    for (let i = 0; i < video.pixels.length; i += 4) {
      let r = video.pixels[i];     // Red
      let g = video.pixels[i + 1]; // Green
      let b = video.pixels[i + 2]; // Blue

      // Calculate grayscale value
      let gray = (r + g + b) / 3;

      // Set the pixel color to the grayscale value
      video.pixels[i] = gray;       // Red
      video.pixels[i + 1] = gray;   // Green
      video.pixels[i + 2] = gray;   // Blue
      // pixels[i + 3] stays the same (Alpha)
    }

    // Update the video pixels
    video.updatePixels();
  }
}

As for the second goal, however, it was also at this stage that I did a lot of technical idle work. I started to write my method for downgrading the video resolution right away without checking the features of the video.size() method, and ended up with a function that loops through the pixel arrays with a time complexity of O(n^2) (which became very time-consuming when the original video resolution is relatively high):

function videoToMosaic(mosaicSize) {
  // Load the pixels from the video
  video.loadPixels();
  
  // Check if the video has pixels loaded
  if (video.pixels.length > 0) {
    // Clear the mosaicPixels array
    mosaicPixels = new Uint8ClampedArray(video.pixels.length);
    
    // Loop through the canvas in blocks
    for (let y = 0; y < height; y += mosaicSize) {
      for (let x = 0; x < width; x += mosaicSize) {
        // Calculate the average color for the block
        let r = 0, g = 0, b = 0;
        let count = 0;

        // Loop through the pixels in the block
        for (let j = 0; j < mosaicSize; j++) {
          for (let i = 0; i < mosaicSize; i++) {
            let pixelX = x + i;
            let pixelY = y + j;

            // Check if within bounds
            if (pixelX < width && pixelY < height) {
              let index = (pixelX + pixelY * video.width) * 4;
              r += video.pixels[index];       // Red
              g += video.pixels[index + 1];   // Green
              b += video.pixels[index + 2];   // Blue
              count++;
            }
          }
        }

        // Calculate average color
        if (count > 0) {
          r = r / count;
          g = g / count;
          b = b / count;
        }

        // Set the color for the entire block in the mosaicPixels array
        for (let j = 0; j < mosaicSize; j++) {
          for (let i = 0; i < mosaicSize; i++) {
            let pixelX = x + i;
            let pixelY = y + j;

            // Check if within bounds
            if (pixelX < width && pixelY < height) {
              let index = (pixelX + pixelY * video.width) * 4;
              mosaicPixels[index] = r;        // Set Red
              mosaicPixels[index + 1] = g;    // Set Green
              mosaicPixels[index + 2] = b;    // Set Blue
              mosaicPixels[index + 3] = 255;   // Set Alpha to fully opaque
            }
          }
        }
      }
    }

At the end of the day, it turned out that I could simply lower the hardware resolution with video.size() method:

video.size(50, 50); // Set the size of the video

Random Points Generated Based on Probability

Next, I wrote a prototype to see how the mosaic of probabilities could guide the random generation. The core code at this stage is to map the x-y coordinates on the canvas to the mosaic pixel array and map the grayscale value to probability (the greater the grayscale, the lower the probability, as the brighter the mosaic, the fewer the points):

function mapToPixelIndex(x, y) {
  // Map the y-coordinate to the pixel array
  let pixelX = Math.floor((x + windowWidth / 2) * j / windowWidth);
  let pixelY = Math.floor((y + windowHeight / 2) * k / windowHeight);
  
  // Ensure the pixel indices are within bounds
  pixelX = constrain(pixelX, 0, j - 1);
  pixelY = constrain(pixelY, 0, k - 1);
  
  // Convert 2D indices to 1D index
  return pixelX + pixelY * j;
}

function shouldDrawPoint(x, y) {
  let index = mapToPixelIndex(x, y);
  let grayscaleValue = pixelArray[index];
  
  // Convert grayscale value to probability
  let probability = 1 - (grayscaleValue / 255);
  
  // Decide to draw the point based on probability
  return random() < probability; // Return true or false based on random chance
}

A Probability Webcam in 3D space

‘Eventually’, I incorporated all the components together to try out my initial vision of creating a fluid, responsive, and vague but solid representation of images from the webcam with arrows generated based on probabilities flying towards an axis plane.

Unfortunately, although the product with many tuning of variables like the amount of arrows, the spread and speed of the arrows, and the lapse of arrows on the axis, etc., there could be a vague representation captured (aided by re-mapping & stretching out the probabilities with more contrasting grayscale values), the huge amount of 3D objects required to shape a figure significantly undermines the experience of running the product.

function applyHighContrast(array) {
  // Stretch the grayscale values to increase contrast
  let minVal = Math.min(...array);
  let maxVal = Math.max(...array);

  // Prevent division by zero if all values are the same
  if (minVal === maxVal) {
    return array.map(() => 255);
  }

  // Apply contrast stretching with a scaling factor
  const contrastFactor = 6; // Increase this value for more contrast
  return array.map(value => {
    // Apply contrast stretching
    let stretchedValue = ((value - minVal) * (255 / (maxVal - minVal))) * contrastFactor;
    
    // Clip the value to ensure it stays within bounds
    return constrain(Math.round(stretchedValue), 0, 255);
  });
}

Besides, the 3D space did not benefit the demonstration of this idea but hindered it as the perspectives of the arrow farther away from the focus would make them occupy more visual space and disturb the probability distribution. 

A Probability Webcam in 2D space

At the end of the day, after trying the ortho() method in the 3D version (which makes the objects appear without the affecting perspectives), I realized that reconstructing a 2D version was the right choice to better achieve my goals.

In this latest 2D version, I gave up the idea of drawing the axis plane and introduced the concept of probability distribution affected by ‘mic level’.

function setup() {
  ...

  // Create an audio from mic
  audio = new p5.AudioIn();
  audio.start(); // start mic
}

function draw() {
  ...

  // Map the audio level to contrast factor
  let level = map(audio.getLevel(), 0, 1, 1.05, 30);
  // Apply high contrast transformation
  pixelArray = applyHighContrast(pixelArray, level);
  ...
}

function applyHighContrast(array, contrastFactor) {
  // Stretch the grayscale values to increase contrast
  let minVal = Math.min(...array);
  let maxVal = Math.max(...array);

  // Prevent division by zero if all values are the same
  if (minVal === maxVal) {
    return array.map(() => 255);
  }

  // Apply contrast stretching with a scaling factor
  return array.map(value => {
    // Apply contrast stretching
    let stretchedValue = ((value - minVal) * (255 / (maxVal - minVal))) * contrastFactor;
    
    // Clip the value to ensure it stays within bounds
    return constrain(Math.round(stretchedValue), 0, 255);
  });
}

reflection

TBH, the technical explorations did consume much of my time this week, and it came to me later to realize that I could have thought of more about the theme ‘loop’ before getting started – as it appears now to be a very promising topic to delve deeper into. Nevertheless, I believe our workflow of production could be like this from time to time, and it is crucial to maintain this balance thoughout.

 

 

 

Week 2 – Bubbles.

CONCEPT:

While walking across campus, I saw children blowing bubbles with their families. This simple, joyful scene transported me back to my own childhood, when my brother and I would spend hours blowing bubbles in the park. While I was in front of the screen, I thought about the patterns I recently saw during the week — and then I was reminded of the bubbles. I was then propelled this wave of nostalgia, inspiring me to incorporate the playful essence of bubbles into my piece. 

 

CODE:

In terms of coding, I was particularly challenged by the randomness factor. I was unsure of how to produce bubbles at random places, sizes, and even colours. Therefore, I looked for assistance on the p5.js website.: https://p5js.org/reference/p5/random/ to understand the syntax of the ‘random’ feature. After relentless experimenting, I was able to randomise the selection of colours, which I had little idea on how to approach. I was particularly proud of it. In terms of design, I removed the stroke to add some sort aesthetic to the bubbles, and I changed the background to a light blue, to resemble the sky.

 

for (let i = 0; i < 20; i++ ){
  
  let x = random(width);
  let y = random(height);
  let r = random (10, 50);
  
  fill(random(250), random(250), random(250))
  
  noStroke()
  
  ellipse(x,y,r)
  

REFLECTION:

This assignment really challenged what my understanding of art is, and also made me realise how often we are surrounded by ‘art’ – no matter how subtle, quotidian, or ordinary it may be. Perhaps, if I were given the opportunity to further it, I may add some user interactivity such as popping the bubble and slowing down the speed.

WORK:

 

Reading Reflection – Week #2

For me, Casey Reas’ talk on the balance between randomness and control in artistic process appeared to be extremely motivational and spot-on. Although Reas does not provide a definitive answer on where this balance lies, the ideas he presents are still compelling and thought-provoking. For instance, I loved the way in which he highlighted the beauty of imprecision, suggesting that it stems from nature itself (which unexpectedly reminded me of the recent reading from another class on the beginning of life on Earth). To prove his point, Casey refers to the “randomness in order” seen in the artwork that demonstrates protein molecules communicating within a cancer cell. This non-obvious connection between the chaotic patterns of nature and those found in computer programming was truly eye-opening.

As I begin to work on my own creative projects using coding, I am constantly reflecting on how much I will rely on chance. For me, randomness is simply a tool to help achieve a creative goal, but it is by no means the most decisive factor. I firmly agree with Reas that artistic choices involve a little magic of chance, but they are mostly guided by thoughtful decision-making about the final result. This applies to all forms of art, as the artistic process is always shaped by stylistic choices, vision, and ideas that arise from the creative mind long before the actual craft begins. Henceforth, there is no “chance” in the foundational decision of what the work will make the viewer feel. The uncertainty only lies in the ways the effect is reached: that’s where embracing the beauty of chance and experiment becomes so handy.

Reading Reflection – Week #2

Reflecting Eyeo2012 lecture by Casey Reas

Casey Reas starts with the statement that nature constituted the chaos in the world, but after, order was introduced by God. I was surprised by such an idea, because in my worldview people are the ones causing chaos in neatly ordered nature. Learning on biology classes about different systems that existed in nature, like nutrient cycling, I was thinking about humans intervening in these structures and causing disorder. But, now, looking back to Reas’ statement, I feel like maybe it is God and humanity who decided to order all the processes in our ecosystem, so that it would be easier for us to live with them. Moreover, I started thinking about how exactly artists are maintaining order with their works. Don’t they question the initial order of things to break through it? I felt like art was always about the protest, but now I do think I was partly wrong. 

Looking at the artworks that experiment with total randomness in images, I really liked how the same patterns get transformed to different mediums and it adds to the idea of a chance and minimum control of future image (07:07 about process 18). In addition, I couldn’t fully understand what’s the optimum balance between total randomness and complete control, because some works used a little bit of randomness and mostly decision making, which gets us to the point that we can control the amount of randomness, and it is a contradictory statement at its core. 

I really liked the author’s throwback to the past interactions of artists with a chance. It reflected in me in a sense that previous generations were highly afraid of chaos or anarchy, which is why destruction of an order in artworks was considered a radical gesture. But, as previous generations, we did experience different wars and uncertainties, and I think what makes us different from them and what makes artists be more confident and open about using randomness in arts, it is a sense of freedom and being okay with not knowing everything. Before I didn’t really think about historical events being influential on appealing to order and chaos, but now I do. 

In the video, the quote of Richter: “Chance is always planned, but also very surprising” resonated with me because chance is needed to introduce something different and disruptive. 

Observing Mondrian’s work, I didn’t expect to understand that order actually does not limit emotion and spirit, even in total order there is a room for interpretation and imagination. Moreover, looking at different algorithms presented by Reas, an association with the game PacMan came to my mind, since it also employed balance between order and randomness.

Reading Reflection: Casey Reas

One thing that I, as an art history major, really enjoyed about Casey Reas’s talk on chance operations is the way he connected the topic to the Dada movement in art, referencing the desire of artists to break away from the pre-World War I  conventions of logic and reason. However, as Reas proposes applying the same elements of randomness through generative computer algorithms to create art, I couldn’t help but begin to question its compatibility with my understanding of what constitutes art.

To me, art is something birthed by human deliberation; it encompasses the story, the soul of the artist. When we leave it to chance operations to work and create independently of the intents of the human artist, can we still consider it a meaningful, artistic creation? But just as Jackson Pollock was the one waving his brushes with much force for the paint droplets to create random patterns, the programmer is the one who sets up and sends the computer programs into motion in the first place, allowing these chance operations to create the unexpected. These pieces are not possible without the programmer setting the parameters, and while I do not have a definitive answer about whether this makes them real artists or not, I think it’s nonetheless interesting to see how the role of an artist evolves over time.

Assignment 2: Faux Sound Wave

Concept:

  • The idea for this assignment is to replicate the look of sound waves as they would appear in computer applications: comprised of multiple parallel vertical lines that fluctuate in length to reflect changes in volume. While it is currently beyond the scope of my coding capabilities to create something that actually responds to external sound input, it would nonetheless be a good opportunity for me to practice using for() loops to generate repetition as well as randomized elements.

Highlight:

  • I am proud of having utilized the random() function within the for() loop in order to generate rectangles that are in fixed positions but have randomized lengths that change every time the frame refreshes, replicating the look and movement of a sound wave.
for(let x=0;x <= width; x +=15){
  noStroke();
  fill(random(0,50),random(150,200),random(30,255));
  rectMode(CENTER);
  rect(x,200,10,random(20,200));
}

Sketch:

Future improvements:

  • The point of sound waves being depicted as such in computer programs is to reflect the volume of the sounds being recorded. In the future, it would be interesting to have the lines respond to various inputs such as clicking, cursor hovering, and sound.

Reading Reflection – Week 2

I found Casey Reas’ Eyeo talk to be really interesting because it gave me a new perspective on how technology can create art. Although I would consider myself a fairly creative person, recently I’ve felt surprisingly restricted by how I can use code to make art. Reas’ work, in particular the one that represented the movement of cancer cells inspired me because it showed how interpretive art can be and also the unique ways in which we can reference aspects about life to influence what we create.

I also found it interesting how Reas distinguished between pieces he deemed complete and others he did not. He labeled a few of them as rather simple or mundane but as a viewer they seemed complete to me. Although this seems rather insignificant I believe it highlights how personal the artistic experience is to an artist. This acted as a reminder for me to work with intention because once I have a goal in mind, like Reas, I believe it will be easier for me to deliberately work on my projects with creative freedom while still having goals in mind.

All in all, I enjoyed the refreshing prospective Reas provided through displaying his own work and hope to apply it to my own methods. Seeing the wide array of examples and applications has not only given me ideas for how I might approach this week’s project but also gives me more confidence to freely explore future developments.

Week 2: Loop Flowers

For inspiration, I found some online references with patterns created by combining similar shapes. Most of these combinations resulted in forms that resembled flowers.

Flower Pattern

I drew a connection between using a shape multiple times to draw a pattern and using loop in p5js. My idea is using the same shape, with elements of randomness to create a different flower every time the user interacts with the canvas.

Challenge

When creating this piece, what I found particularly challenging was using the rotate() function to position the petals around the center (the mouse cursor). However, the rotate() function in p5.js only allows rotation around the origin (0,0), so I had to use translate() to change the origin to the mouse cursor’s position. This quick fix created a problem later when I wanted the flowers to remain permanently on the canvas instead of disappearing every time the mouse was released.

For drawing the flowers, I created two functions: one to draw flowers with squares and one with ellipses. They are similar in essence except for the change in shapes used to draw.

function squareFlower(x,y,size,fcolor){
  //repeat drawing the square with the rotation of angle until the 360 degree is full
  for(let i =0; i<(360/angle); i++){
    rotate(angle);
    stroke(fcolor[0], fcolor[1],fcolor[2]);
    fill(fcolor[0], fcolor[1],fcolor[2],fcolor[3]);
    rect(x,y,size);
  }
}

Finally, I included random attributes like shape, petal size, and angle of rotation to create different flowers. I also adjusted the opacity of the shapes to create a more interesting composition. To make it look more like flowers, I implemented an increase in shape size over time when the mouse is pressed, creating a blooming effect.

Final Sketch

Reflection

After finishing the sketch, I tried to make the flower appear permanently on the screen instead of disappearing, but I couldn’t because the flowers are drawn based on their rotation around the origin (which changes for each flower). When I revisit this in the future, I want to change the drawing of the flowers from using the built-in rotate() function to using math to calculate the new coordinates. This will allow the flowers to be drawn independently of the origin’s location. Another issue is that some shapes in the flowers are overlapping, which I think stems from the calculation of the rotation angle. I will also try to improve this calculation in future sketches.

Assignment 1 Self Portrait

Concept: 

For my self portrait I wanted to do something slightly unconventional. Joan Miro is an artist that I really like and I thus decided to do my self portrait in his unorthodox surrealist style. I wanted to have fun background elements commonly seen in his artwork such as the crescent moon and a star.

Something I’m proud of:

Something I’m proud of for this assignment are the random curves across the whole portrait, and especially drawing the crescent moon. These aspects took me the longest time to figure out and are thus the ones I am most proud of.

function details() {
  
  fill(255,255,0);//drawing a crescent moon shape by drawing two intersecting circles and having one of them be the color of the background
  //this effect makes it so that the second circle cuts the first one to make the moon shape
  noStroke();
  ellipse(150,630,100,100);
  fill(240, 248, 255);
  ellipse(170,630,100,100);

  //drawing the diagonal lines for the eyebrows
  stroke(0);
  strokeWeight(4);
  line(300, 300, 450, 400);
  line(500, 400, 650, 300);

  //drawing the yellow curve around the mouth
  noFill();
  stroke(255, 215, 0);
  strokeWeight(8);
  beginShape();
  vertex(250, 550);
  bezierVertex(350, 500, 450, 700, 550, 550);
  endShape();
}

Final Product:

Reflections:

I enjoyed working on this assignment quite a lot, it was a fun experience juggling between taking inspiration from Miro and then also trying to add my own personality into the portrait so I could make something I could be truly proud of. An aspect that I would like to work on in the future is adding some sort of interactivity maybe with eye movement or some background features morphing into others to further portray the dreamlike essence of Joan Miro.

Assignment 1: Self-portrait – CATWOMAN

My concept 

  • There were two main components that I wanted to explore with this project: creating a background and creating the figure. I drew a rough sketch of what I wanted to do. I wanted to create a background of the night sky, and in the foreground I wanted the figure of a cat/woman. My end product became very different from this preliminary sketch, but I had a rough idea of what I wanted to do. But I knew I wanted to create myself as my spirit animal, by blending my physical features with that of a cat. The choice of placing myself in space is because I simply love the idea of staring into the night sky and seeing the moon and stars, and to surround myself with these objects would be symbolic of the way I enjoy wondering, imagining, and dreaming. 

Reflection 

  • I began with the background, the sky’s color, and the moon and stars. Then, I changed my mind and wanted my catwoman to be in space, so I created her planet and other planets around her. This was more time consuming than I originally thought it would be. I had to place the stars, I had to choose where to position the other planets, and I had to create the details of the main planet (the holes), which I later had to adjust because I put the catwoman on top and wanted the placement of these objects around her to look ideal. 
  • After finishing the background, I jumped into creating my catwoman. I started with her head, neck, and body. I started becoming worried, because I wasn’t sure about their orientation. But then, I started adding the details: eyes, mouth, nose, hair. This proved particularly difficult, with each component taking longer than the last. I didn’t like how my piece looked, but continued adding details: the cat ears, whiskers, eyebrows, shirt, blush, necklace, astronaut helmet. In the end, I came up with my finished product that I have grown to like. I realized that the more time and more details to put into it, the more it begins looking like something worthwhile, and I realized that slowly the piece will start to come together. It won’t be as I imagined, but will be what I was able to make given what I have learned as well as what I had researched. 

Sketch 

Code I’m proud of 

Creating the hair for my portrait took me a while. I had to layer it and I had to think about where I was able to write the code for the different layers, I had to copy-paste it into later sections so the stars/ears did not overlap. I used a circle to create the first layer of the hair. I then wanted the hair to look like a bob-cut, so I created a rectangular cut-off with the color of the planet behind the figure so that it would blend. I then added the bangs. In a later line of code, I framed the face of the figure, by creating two rectangles and placing it on the sides of the face to look as if the hair is over her face.

//hair
fill('#471706');
noStroke();
ellipse(145,277,150,150);
fill('#A3A3A3');
rect(90,330,110,30);
//head
noStroke();
fill('rgb(201,139,76)');
circle(145,270,98);
//hair bangs
fill('#471706');
noStroke();
ellipse(125,230,50,40);
ellipse(167,230,50,40);
//hair to frame face
fill('#471706');
noStroke();
rect(94.25,242,10,70)
rect(186,240,10,70)

Ideas for future work/Improvements 

  • I would really love to add animation to this work. I wasn’t sure how to do it so I looked at videos and articles online, but I did not completely understand the code for it, so I decided to leave it out for this assignment, but I look forward to implementing it in future projects. I wanted the background to not be so static, possibly with the moon phases and shooting stars, and possibly the rotation of the planets in the background. Regarding the catwoman, I’d want her to possibly wiggle her tail and move her ears. I feel like those animations would add to the portrait, by enhancing the background of outer space and the choice of a cat as my spirit animal.