Week #4 – 2000s qwerty

Introduction

For this assignment, I tried to challenge myself a bit more by implementing a different concept. I spent some days thinking about what I could implement for this assignment, for example, I was thinking about making either an abstract pattern or a “musical face”. Although, one thought led to another, and I arrived at mechanical keyboards and the 2000s aesthetic!

Full-screen version: Full-screen version

The concept

Since in this week we saw the concepts of oscillation and, mainly, we had to be inspired by the artist Memo Akten, it was necessary to implement something similar. That means, almost synchronized movements via mathematical functions, and peaceful timed sounds.

As mentioned, I was thinking in implementing an abstract design, but I found myself more interested in creating a more cohesive canva. This cohesive design combines things I am fond of, which include 2000s aesthetics, nostalgia and computers.

Old key caps.
Figure 1. 2000s keyboard uploaded by an anonymous user on r/MechanicalKeyboards.

Highlight of the code I am proud of

The main challenge of this code was to implement the sound system. I am not very proud of some parts of the code of this system, since I had to implement quick fixes. Nevertheless, one of the key features of this is that, according to the letter specified, it will assign a certain value to start the audio for the key, since all the sound effects are integrated into a single file.

Since all the system of the audio is distributed in the class Keycap, I will share the corresponding code.

class Keycap{
    constructor(x,y,letter){
        this.sound = loadSound("files/sound.ogg"); //The only solution to avoid overlaps and sudden sound stops was to do this.
        this.position = createVector(x,y);
        this.w = 40;
        this.h = 40;
        this.letter = letter;
        this.soundstart = 0;  //This variable will keep a value to jump the sound to play the sound.
        this.sound_played = 0;
        this.sound_time = 0; ///Check by how much time the sound played.
    }

    //Displays and also determines which sound to play according to the key cap.
    display(){
        push();
        noStroke();

        fill(235, 235);
        rect(this.position.x-5,this.position.y-2,this.w+10,this.h+10);

        fill(224, 223);
        rect(this.position.x,this.position.y,this.w,this.h);


        strokeWeight(30);
        textSize(23);
        fill(0);

        switch (this.letter) {
            case "q":
                text("q",this.position.x+15,this.position.y+25);
                this.soundstart = 2.90;
                break;
        
            case "w":
                text("w",this.position.x+15,this.position.y+25);
                this.soundstart = 4.90;
                break;

            case "e":
                text("e",this.position.x+15,this.position.y+25);
                this.soundstart = 5.90;
                break;
            
            case "r":
                text("r",this.position.x+15,this.position.y+25);
                this.soundstart = 6.90;
                break;

            case "t":
                text("t",this.position.x+15,this.position.y+25);
                this.soundstart = 7.90;
                break;

            case "y":
                text("y",this.position.x+15,this.position.y+25);
                this.soundstart = 8.90;
                break;

            default:
                break;
        }
        pop();
    }

    keypressed(){

        //Highlight that the key was pressed.
        push();
        strokeWeight(10);
        fill(235, 235, 235);
        rect(this.position.x-5,this.position.y-2,this.w+10,this.h+10);
        fill(235, 235, 235);
        rect(this.position.x,this.position.y,this.w,this.h);
        textSize(15);
        fill(0);
        pop();
    
        ///Then play a sound according to the letter.
        this.play_sound();
    }
  
    play_sound(){
      if (this.sound_played == 0){
        this.sound.play();
        this.sound.jump(this.soundstart);
        this.sound_played = 1;
      } 
    }
  
    stop_sound(){
        this.sound.stop();
        this.sound_played = 0;
      }
  
}

In a very summarized version, what is happening is the following:

      • According to the letter specified when creating the class, and in the constructor, it will go through a switch() case to determine the start point of the audio.
      • Once one of the moving rectangles enters one of the key caps (or the range of it), will activate the function keypressed().
      • Once keypressed() is started, it changes the visual design of the key caps for a short moment, to then play the sound via the function play_sound().
      • Once the function is called, it determines if the audio is currently playing via the variable this.sound_played, and once it is determined that it has not started, it will play using the specific start point. Likewise, to avoid overlapping, the audio then assigns this.sound_played to 1.
      • Once the moving rectangle leaves the area of the key cap, it calls the function stop_sound().

Reflection

Compared to last assignments, I am happy with the end result. It feels that the ideas that I wanted to implement on my mind could become real. Nevertheless, I would say that for future assignments I would like to improve upon the code, since I feel it is a bit redundant this time, as well as not very optimized.

References

    1. 17.3: Timing, Jumps and Cues – p5.js Sound Tutorial
    2. CherryMX Black – ABS keycaps
    3. JavaScript Switch Statement
    4. Late-night Sim City 2000
    5. text()
    6. Where can I get a windows keycap that looks like this?

 

Week #3 – Magnetic Waves

Introduction

For this week, I initially planned to create marbles falling from a platform and landing on a magnetic field, where they would accumulate over time and collide with each other. At first, this idea seemed interesting to implement, but once I realized it was going to take more time than I originally planned, I had to scale down my ambition.

However, while trying to implement the original idea, I discovered an interesting behavior between three attractors. That’s when I decided to create the following sketch:

Note: You can interact by holding the left mouse button in the Canva.

The concept

As mentioned, the idea transitioned from a fun physics simulation with marbles and magnetic fields to a set of movers accumulating around randomly defined attractors.

There are 3 attractors in total, each with a random X and Y position determined by the width and range of the canvas. Additionally, the movers can collide with the borders of the scenario, and they also have friction as an extra feature.

Initially, the 3 attractors were fixed, as shown in the following full-screen sketch:  Test #1 of Assignment #3. However, this setup was not dynamic enough, and the movers never collided with the borders of the screen. Therefore, it was necessary to make the attractors’ position constantly random.

As an additional note, the colors of each mover are defined by Perlin noise.

Highlight of the code I am proud of

One segment of code I am most proud of is the following:

This part of the code is found in the file sketch.js, inside the draw() function.

if (attractors[0].g > 0){
        attractors[0].g -= 1;
        attractors[1].g += 1;
        
    } else if (attractors[1].g > 0){
        attractors[1].g -= 1;
        attractors[2].g += 1;
        

    } else if (attractors[2].g > 0){
        attractors[2].g -= 1;
        
      
    //Reset first value and the rest of positions of the attractors.
    } else {
        attractors[0].position.x = random(width);
        attractors[0].position.y = random(height);


        attractors[1].position.x = random(width);
        attractors[1].position.y = random(height);

        attractors[2].position.x = random(width);
        attractors[2].position.y = random(height);

        attractors[0].g = 70;
        attractors[2].g = 0;
    }
}

What this process does is, depending on the movers’ positions, increase the universal gravitational constant (G) of the next attractor while decreasing that of the current one, creating a smooth transition between attractors. When the final attractor is reached and its gravitational constant is reduced to 0, the process restarts by randomly assigning new positions to each attractor and redefining their gravitational constants to prevent any issues.

Reflection and ideas for future improvements

I initially thought it would take me more time to grasp the concepts. While there are interesting patterns I would love to create in the future with the movers and attractors, for now, I am glad I am understanding the concepts presented in class, which allows me to experiment with them. That said, here are some improvements I would like to explore in the future:

      • Experiment with parallel attractors and movers. In other words, have two sets of attractors and movers that can interact with each other, instead of a single group of movers interacting with two attractors at a time.
      • Use the collisions and gravity more creatively to add complexity to the designs.
      • Add more user interaction to manipulate the canvas, along with clear visual cues.

References

Most of the code that I used as a guide is from the following video (which is also provided in the weekly schedule):  Mutual Attraction by The Coding Train

Week #2 – Some birds and a tree

Introduction

To get inspired for this week assignment, I took a walk around the campus. I had many ideas in my mind about what I wanted to do; some of them were about trying to imitate the movement of ants or the light of the sun. Although, something caught my interested when I stumbled upon this view:

That is when I realized what I wanted to do.

The Concept

Once I observed this tree populated by many birds, I wanted to recreate both the movement of the leaves, of the tree, and the path the birds take to reach it. At first, I was intimidated to think about this, since it would be very hard to simulate a tree with moving leaves. Nevertheless, I was intrigued.

To reach a satisfactory conclusion for the project, first I had to make a quick sketch about what I wanted to do:

Mockup
Figure 1. Mock-up of the moving trees and how they are going to move according to region.

From what I analyzed in this sketch, I would have to do the following:

    • Simulate many leaves moving at the same time without lagging the computer.
    • Make a believable movement using velocity and acceleration.
    • Populate, randomly, each region and with a different pattern (that is, how they are going to generate according to the range specified).
    • Understand how the birds will interact after the leaves are populated.

Preparing the scenario

Compared to last week’s assignment, I felt that it was too abstract and needed a bit more of style in order to be more presentable. From this experience, I wanted to avoid what I did previously and improve upon it, thus, I wanted to develop a more “alive” canvas.

For this, I needed to create first the scenario that was in symphony with my then progress done:

Figure 2. Preparing the scenario.

Not only that, but in order to create this “alive” canvas, I also realized we would need audio. So, I extracted the audio from the video I recorded, edited it and then added it to the project:

Figure 3. Preparing the background audio.

The code

According to what we have seen in class at the moment, for this code it was essential to work with vectors. Essentially, the vectors were used for almost everything: The ranges where the leaves spawn, the leaves and the birds.

In order to understand how this is possible, here is a quick explanation of how it works in a sequential order:

    1. Regions are created, manually, with the help of createVector(x,y). These regions are stored in the class Leaves. The reasoning of creating regions is to simulate, as accurate a possible with the limitations of p5.js, the movement of the leaves without creating lag and with enough diversity to create the illusion.
    2. After the regions are set, the leaves now start to spawn in each region. There are a total of 5 different regions and, by default, 80 leaves by group. The leaves themselves are represented by green circles that walk through transparency with the help of the Perlin movement. Not only that, but the leaves move by accelerating forwards and backwards in their region.
    3. After the regions and leaves are spawned, the birds can start appearing. To avoid creating a lot of circles in the screen, I decided to only draw one black circle for the birds. They will appear one by one from either the left or right part of the screen. The bird will select a region, randomly, as the endpoint. As mentioned, the birds are represented as black circles that will appear as medium-sized and will be further decreasing to create the illusion of the bird going inside the tree. Once the size of the bird reaches 0, it will generate another bird.

Important note: In order to simulate all of this believably, I had to lock the frame rate to 10. Since if it left with a default of 60, it will look that the leaves and birds are moving at super speeds.

What I am mostly proud of

I am proud that I could simulate how leaves move (at least partially). This was the most challenging part of this work and the one that needed to be addressed first. I took some examples from the class in order to understand how to create this movement. Although, sometimes due to not understanding how it fully worked, the leaves would go out of the Canva, move weirdly, extremely fast and unnatural or just not show at all (because they were going so fast that our eyes could not perceive the moment it left the screen).

Here is a quick example of the code that I used for the movement of the leaves:

// ----- Move leaves according to region. -----

        for (let i=0; i<amount_of_leaves; i++){
            //Taken from the example taught in class.
            let dir = p5.Vector.sub(this.range1, this.p1_leaves[i]);

            dir.normalize();
            dir.mult(0.05);

            this.acceleration = dir;
            this.velocity.add(this.acceleration);
            this.velocity.limit(5);
            this.p1_leaves[i].add(this.velocity);
        }


        for (let i=0; i<amount_of_leaves; i++){
            //Taken from the example taught in class.
            let dir = p5.Vector.sub(this.range2, this.p2_leaves[i]);

            dir.normalize();
            dir.mult(0.05);

            this.acceleration = dir;
            this.velocity.add(this.acceleration);
            this.velocity.limit(5);
            this.p2_leaves[i].add(this.velocity);
        }

        
        for (let i=0; i<amount_of_leaves; i++){
            //Taken from the example taught in class.
            let dir = p5.Vector.sub(this.range3, this.p3_leaves[i]);

            dir.normalize();
            dir.mult(0.05);
 
            this.acceleration = dir;
            this.velocity.add(this.acceleration);
            this.velocity.limit(5);
            this.p3_leaves[i].add(this.velocity);
        }

Although, I am not very proud by the amount of for statements…

Similarly, for the movement of the birds the code was similar, but easier to understand since I knew how it worked:

fly(rng_number){
      //Decide if the bird on the left or right will flight. After that, decide on the final destination to then apply the motion.
        if (rng_number == 1){
            if (this.final_region_destination == 1){
                this.dir = p5.Vector.sub(leaves.range1, this.bird1);
            } else if (this.final_region_destination == 2){
                this.dir = p5.Vector.sub(leaves.range2, this.bird1);
            } else if (this.final_region_destination == 3){
                this.dir = p5.Vector.sub(leaves.range3, this.bird1);
            } else if (this.final_region_destination == 4){
                this.dir = p5.Vector.sub(leaves.range4, this.bird1);
            }
            
            this.dir.normalize();
            this.dir.mult(0.6);

            this.acceleration = this.dir;
            this.velocity.add(this.acceleration);
            this.velocity.limit(10);
            this.bird1.add(this.velocity);

            if (this.t <= 0){
                this.restart();
            }

        }

        else if (rng_number == 2){
            if (this.final_region_destination == 1){
                this.dir = p5.Vector.sub(leaves.range1, this.bird2);
            } else if (this.final_region_destination == 2){
                this.dir = p5.Vector.sub(leaves.range2, this.bird2);
            } else if (this.final_region_destination == 3){
                this.dir = p5.Vector.sub(leaves.range3, this.bird2);
            } else if (this.final_region_destination == 4){
                this.dir = p5.Vector.sub(leaves.range4, this.bird2);
            }
            
            this.dir.normalize();
            this.dir.mult(0.6);

            this.acceleration = this.dir;
            this.velocity.add(this.acceleration);
            this.velocity.limit(10);
            this.bird2.add(this.velocity);

            if (this.t <= 0){
                this.restart();
            }

        }

Again, not very proud with how copied and pasted it looks…

The Sketch

So, after all of this explanation, what is the final product? Well, there is the final sketch.

Note: The “white background effect” is intentional. Also, this Canva has background audio, no mouse interactions and a default of 10 frames per second.

Full-screen version: Go to the Full-screen version

Reflection and future improvements

I am mostly happy to how this assignment concluded. I feel that I learned new things about the programming world as well as how to interpret movements in nature. Although, there are some improvements I want to make for future works:

    • Even though this sounds nonsensical, I would like to avoid using too many in-class examples for my codes and challenged myself a bit more with what I can find out of the box.
    • Avoid that the circles accumulate all together if the Canva is left running for a few minutes.

Credits

Some references I used for help are the following:

FrameRate reference: https://p5js.org/reference/p5/frameRate/

Sound reference: http://p5js.org/reference/#/p5.SoundFile

 

And the external programs I used are:

To create the background: GIMP.

To prepare the audio: Audacity.

Week #1 – Reading Reflection

This reading made me think about how our systems are composed and the relation between them. The introduction gives an example with ants and how a singular ant is a rather simple entity with a set of simple tasks. Nevertheless, if put together a multitude of ants, the number of tasks and its complexity increases. Similar examples can be found even in even more complex things, such as computers. A singular computer is capable of doing an almost infinite set of tasks if properly handled, the same can not be said for a set of computers since the difficulty of handling them increases.

The reading also made me realize that in our life everything is part from a sequential process of parallelism, iterations, and adaptation. For example, in my life I had to do multiple things at once in order to get something, I had to continue fighting for what I wanted continuously and adapted into new environments. This sequential process will be cycled over and over again; nothing to be afraid of, it’s part of life.

And lastly, this reading made me reflect on how we can interconnect between different languages (using the different sciences as an example from the book). For example, something that only artists could do now is able to be imitated and understood by people who lack the advanced knowledge. In other words, if I want to paint the Mona Lisa, the computer could tell me the exact steps that were taken in order to reproduce such a piece. This is thanks to this “translation” process.

Week #1 — Dynamically (and randomly) variable “Flower Pot”

The concept

Since in class we view the topic of motions with some formulas, I initially was confused as to how it worked. I and the mathematics, we do not get along very well. Although, I did want to try the new material taught with the following code. So, in this occasion, I present: The Dynamically (and randomly) variable “Flower Pot”.

The Pelin walker presented in class reminded me of how plants can grow overtime: beautiful and with a bit of unproductiveness. With this in mind, I had some ideas related to the growth of plants and a flower pot.

How it works

The code starts by:

1. Preparing some Pelin walkers and keeping them inside the flower pot (or the white rectangle).

2. Decreasing (as in increasing the height) of the Pelin walkers.

3. Once the Pelin walkers reached the top of the flower cup, they began their iconic movement.

4. When the Pelin walkers are moving, their color uses the same motion to shift the RGB values.

The interaction

Once the movement phase start, the Pelin walkers can be altered by the position of the mouse. That is, depending on which X or Y coordinates the mouse is, the Pelin walkers can either continue growing, stop completely or avoid the pointer. Not only this, but to avoid overgrow, there is a chance (about 1%) of stopping the Pelin walkers from continue moving and if the user decides, it can reset the canva by pressing left click.

A highlight of the code I am proud of

The code was a bit hard to make it work, since the implementation of the interaction with the mouse and the way the Pelin increases within the margins was challenging:

class Walker {
    constructor(x, y, w){  //It is called initial X and Y since is the initial start point.
        //This has to generate under the condition of being inside the box
        this.x = x;
        this.y = y;
        this.w = w;

        //Variables for the pelin movement.
        this.tx = x;
        this.ty = y;
        this.lastposition = 0;
        this.rise = 0; //Controls the map to allow the plant to growth.
        this.free_x = 0; //Frees X space
        this.free_y = 0; //Frees Y space.  Both of them helps to create the illusion of growth.

    } 
    draw(){
        push();
        noStroke();
        fill(map(noise(seed1), 0, 1, 0, width),  map(noise(seed2), 0, 1, 0, height), map(noise(seed1+seed2), 0, 1, 0, seed1+seed2));
        ellipse(this.x, this.y, this.w);
        pop();
    }
             //!!!!!!!!!!!!!!!!
    move(){  //Cup class has to be created first in order for this to move. If not, there wil be a crash.
             //!!!!!!!!!!!!!!!!

        this.x = map(noise(this.tx), 0, 1, (cup.x)-this.free_x, (cup.x+cup.w)+this.free_x);
        this.y = map(noise(this.ty), 0, 1, this.lastposition-this.free_x, cup.y-this.free_y);
        this.tx += 0.01;
        this.ty += 0.01; 
    
        //Dynamic probability over here. Basically, according to a number of chance, it dictates if
        //it should be more open or closed

        if (random_number > 1 && random_number < 4){
            if (this.free_y != -1){
                this.free_y -= mouseY/6;
            }
    
            if (this.free_x != -1){
                this.free_x -= mouseX/6;
            }
        } else if (random_number == 1){
            this.free_y = -1;  //Stops growing;
            this.free_x = -1;
        } else {
            if (this.free_y != -1){
                this.free_y += 1;
            }
    
            if (this.free_x != -1){
                this.free_x += 1;
            }
        }
    }
}

Here is the embedded sketch

Remember to press left click to reset!

Reflection and ideas for future work or improvements

Since the code feels unfinished due to the lack of hints and meaningful interaction between the user and the canva, there is a lot that is left that I desired to add:

  • The Pelin Walker actually avoid the cursor in a fluid motion.
  • Useful hints that indicate which sections of the screen allows or stops the growth of the Pelin walkers.
  • Music in the background.
  • An actual white flower pot instead of a white rectangle.

Despite all this, I feel satisfied that I could learn something new; maybe I can implement this technique in the future if there is the need.