Control Structure and Visualisation

Simple Algorithmic Designs

Introducing Control Structure and Visualisation


  • Control Structures and Visualisation

    Introducing the mental model to approach assignments in class

  • Randomness and Noise

    Applying randomness and noise in our work

  • Easing

    Applying easing and using it with randomness

  • Utility Functions

    P5.js functions you may find helpful

Control Structures and Visualisation


Generative design primarily hinges on creating algorithmic structures that can support unknown numbers. This is a difficult task as we need to create an algorithmic structure flexible enough to work with known unknowns to communicate a consistent visual style.

In this course, we separate these two factors into a control structure and visualization. Control structures are the mechanisms that formulate coordinates, color, and other values. Algorithmic structures such as for loops and if statements with unknown inputs coming from random and/or noise would make up your control structure.

Creating Randomness


Randomization of values is used to seed a design system to create generative results. Randomness applied to a structured design system may be difficult to predict but exhibit common themes. This is different than a purely random result which results from the overuse of randomization. The random function returns to us random numbers between a defined range, this can be helpful when we need randomized numbers for a particular purpose such as for coordinates or color. Check the reference to see the uses and syntax of this function.

                
                  // We can use randomness to create x and y coordinates
                  // Lets assign a random value to the variable 'randomX' we can use the two
                  // parameters of the random function to set the range.

                  let randomX = random(100, width-100);
                  let randomY = random(100, height-100);
                
              

Visualising Randomness


Here we have applied a basic visualisation to the random coorindates generated by the 'control structure' shown earlier. This control strucutre is be visualised using ellipses with random fills to form a drawing over time. The random function in this case has been applied to generate numbers between 0 and 255 for our fill values.

                
                  // Our control structure (randomness)
                  let randomX = random(100, width-100);
                  let randomY = random(100, height-100);

                  // Here we set a random fill colour!
                  // Notice how we can call random whenever we need a number
                  fill(random(0, 255), random(0,255), random(0,255), 200);

                  // Remove stroke
                  noStroke();

                  // Draw an ellipse using the random numbers we created earlier
                  ellipse(randomX, randomY, 100);
                
              

Random Walker


Randomness can be applied to seed decisions made by a design system, the random walker uses the result of a random number to decide which way to travel every frame.


              let x, y;

              function setup() {
                background(230);
                createCanvas(windowWidth, windowHeight)
                x = width/2;
                y = height/2;
              }

              function draw() {
                // Use the random values from r to pick the direction to remove
                // Each if statement sends the x or y position of our ellipse in
                // a direction of 4 pixels
                let direction = floor(random(4));
                if(direction == 0) x = x + 4;
                if(direction == 1) x = x - 4;
                if(direction == 2) y = y + 4;
                if(direction == 3) y = y - 4;

                // Here we check if the ellipse has touched the side of the canvas and
                // Put it back on canvas if it has left
                if (x == width)	x = width - 25;
                if (x < 0) x = 25;
                if (y == height) y = height - 25;
                if (y < 0) y = 25;

                ellipse(x, y, 50);
              }
            

Introduction To Random Challenge


Here

Using Noise For Randomness


Here we can see a basic example of the noise function being applied. Over time the x value of the ellipse is being updated with values generated from noise functions. Notice how unlike random the values that noise creates are related. Each number after another is somewhat related so it creates a less chaotic result. Check the reference to see the uses and syntax of this function.

Using Noise For Randomness


The basic example here is extended to visualise the random coordinates being generated.


              // Create a varaible to provide fresh input for our noise function
              let counter = 0;

              function setup() {
              	createCanvas(windowWidth, windowHeight);
              	background(247, 249, 251);
              }

              function draw() {
              	// noise function is given a counter value which will return a number
                // 0 and 1, this is then multiplied by the width so we can see it change
                // over time on our ellipse
              	let noiseValue = noise(counter) * width;

              	ellipse(noiseValue, height/2, 50);

              	// counter increases by 0.005 each time draw funciton loops giving the
              	// noise funciton a fresh number each loop
              	counter = counter + 0.005;
              }
            

Noise and Random Output Compared


On the left the stroke values are being generated by noise and on the right the stroke values being generated by random. We can see how both functions behave over time. Using the slider in the top left we can see how increasing the step size creates greater variaiton between previous values created by the noise function.

From this demonstration we can see that noise is a more orderly method for creating randomness over time. There is a transiton between each new number created unlike using random.

Noise Walker


Using the related numbers that the noise function creates, it can be applied to the x and y coordinates of shape to create unpredictable "organic-like" movement patterns.

              
                let countX = 0;
                let countY = 0;
                let x, y;
                let size = 50;

                function setup() {
                	createCanvas(windowWidth, windowHeight);
                	background(230);
                }

                function draw() {
                  // This is where we get our nice numbers from noise
                  // remember that noise needs a value changing over
                  // time to give us a number
                  // Noise gives us a number between 0 and 1 if we multiply by
                  // width it will lead to smooth numbers moving across the screen
                	x = noise(countX) * width;
                	y = noise(countY) * height;

                	// draw our ellipse using these nice values from noise
                	ellipse(x, y, size);

                	//increase our counter every time the draw loop loops
                	countX = countX + 0.004;
                	countY = countY	+ 0.003;
                }

             
           

Introduction To Noise Challenge


Here

Easing Control Structure


Easing is the smoothing of a transition between two values and can provide structure to your randomly generated values. In this example, we have a visualization of the logic behind easing. Each small red dot represents 5% of the distance between where the ellipse is and where it is going. Using an easing of 5% the ellipse will travel that fraction of the distance it wants to cover every frame. This is why the ellipse slows down when it gets close to its destination.

We use lerp to do the easing calculation for us. Check the reference to see the uses and syntax of this function.

Easing


                

                  let x = 0;
                  let y = 0;

                  function setup() {
                    createCanvas(720, 400);
                    noStroke();
                  }

                  function draw() {
                    background(51);

                    // lerp() calculates a number between two numbers at a specific increment.
                    // The amt parameter is the amount to interpolate between the two values
                    // where 0.0 equal to the first point, 0.1 is very near the first point, 0.5
                    // is half-way in between, etc.

                    // Here we are moving 5% of the way to the mouse location each frame
                    x = lerp(x, mouseX, 0.05);
                    y = lerp(y, mouseY, 0.05);

                    fill(255);
                    stroke(255);
                    ellipse(x, y, 66, 66);
                  }
               
             

Easing Walker


                
                  let x = 0;
                  let y = 0;
                  let targetX, targetY;

                  function setup() {
                    createCanvas(windowWidth, windowHeight);
                    background(230);

                    targetX = width / 2;
                    targetY = height / 2;
                  }

                  function draw() {
                    fill(20);
                    stroke(170);
                    // We are lerping to our random coordinates now

                    x = lerp(x, targetX, 0.05);
                    y = lerp(y, targetY, 0.05);
   
                    ellipse(x, y, 50);

                    // Every 2 seconds or 120 frames update the random coordinates
                    if (frameCount % 120 == 0) {
                      targetX = random(100, width - 100);
                      targetY = random(100, height - 100);
                    }
                  }
               
             

Easing Algorithm Challenge


Here

Using Sin and Cos Structure


              
                let count = 0;
                let x = 0;

                function setup() {
                	createCanvas(windowWidth, windowHeight);
                	angleMode(DEGREES); // Change to degrees for easy math
                	background(247, 249, 251);
                }

                function draw() {
                  // Here we are creating varaibles to remap the values we get from sin and cos 
                	let sinY = map(sin(count), -1, 1, 0.5,1.5) * height/2;
                	let cosY = map(cos(count), -1, 1, 0.5,1.5) * height/2;

                  // The speed which these variables update changes the pattern
                	count += 1;
                	x += 5;

                  // This test sends our ellipse to the far left once off the screen
                	if(x > width+ 50) x = -50;

                	fill(255);
                	ellipse(x, sinY, 50);

                	fill(255);
                	ellipse(x, cosY, 50);
                }
              
            

Helpful Utility Functions


angleMode()


By default when using with any function relating to angles or trigonometry such as sin, cos or rotate (there are many others), p5.js generally refers to angles via radians, in short representing angles as fractions of PI rather than using degrees. For example, an angle of 90 degrees is represented as PI/2 in radians. This can be a fine for many applications however if you are more comfortable working with degrees then we can change the way angles are represented using angleMode()

                
                  function setup() {
                    createCanvas(windowWidth,windowHeight);
                  }

                  function draw() {
                    background(230);

                    angleMode(DEGREES); // Change the mode to DEGREES
                    // In this mode a full oscillation needs the mouse to move 360 degrees or 360 pixels
                    let a = map(sin(mouseX),-1,1,height/4,(height/4*3));
                    ellipse(width/2- 100, a, 50, 50);

                    angleMode(RADIANS); // Change the mode to RADIANS
                    // In this mode a full oscillation needs the mouse to move 2PI or 6.28 pixels
                    let b = map(sin(mouseX),-1, 1,height/4,(height/4*3));
                    ellipse(width/2+ 100, b, 50, 50);
                  }
                
              

floor()


The floor function turns any of of the numbers you give it into whole numbers. It will round down so if you were to give it 3.8 it would return 3. This is quite helpful when working with functions that give us decimal points such as random which may not work nicely when trying to perform certain tests.

                
                  function setup() {
                    createCanvas(windowWidth,windowHeight);
                    frameRate(1);
                  }

                  function draw() {
                    background(230);

                    let random1 = random(2);
                    // the chances of this being true are very low as random gives
                    // us decimal values which mean random1 will rarely be equal to
                    // exactly 1
                    if(random1 == 1) {
                      ellipse(width/2 - 100, height/2, 50,50);
                    }

                    let random2 = floor(random(2));
                    // This test has an appromiate 50% chance to be true as the decimals are being
                    // chopped off the end so we can only get a result of 0 or 1
                    if(random2 == 1) {
                      ellipse(width/2 + 100, height/2, 50,50);
                    }
                  }
                
              

abs()


The abs funciton takes in numbers and always returns them positive. So if you were to put -3 in abs it would return 3.

                
                  function setup() {
                    createCanvas(windowWidth,windowHeight);
                  }

                  function draw() {
                    background(230);

                    let sinMapped = sin(frameCount/100)*height/3;
                    ellipse(width/2 - 100, sinMapped, 50);

                    // Sin usually goes negative however abs always returns the positive values
                    // So this ellipse never leaves the canavs in negative y space
                    let sinMappedAbs = abs(sin(frameCount/100))*height/3;
                    ellipse(width/2 + 100, sinMappedAbs, 50);
                  }
                
              

colorMode()


The colorMode function changes the way that we work with color in P5.js. By default we describe color with RGB however there are times when working with HSB color ranges might be more helpful. HSB = Hue, Sautration, Brightness

                
                  let x = 0;
                  function setup() {
                    createCanvas(windowWidth,windowHeight);
                    background(230);
                    noStroke();
                  }
                  function draw() {
                    // Uses rgb to describe color
                    colorMode(HSB);
                    let hsbColor = map(x, 0, width, 0, 360);
                    fill(hsbColor,100,100);
                    ellipse(x,height/2 - 100, 50);

                    // Uses rgb to describe color
                    colorMode(RGB);
                    let rgbColor = map(x, 0, width, 0, 255);
                    fill(rgbColor);
                    ellipse(x,height/2 + 100, 50);

                    // Move the ellipse to the right
                    x++;
                    // once at the right edge put back on the left
                    if(x > width) x = 0;
                  }