Kevin Riordan and Sharon Yang- Final Project Section C

When many keys are pressed
kzr junginny
When one key is held down
When mouse is hovered over control display

final project code

/*************************************************************************************************************************************************************************************
Kevin Riordan and Sharon Yang 15-104 Final Project Section C kzr@andrew.cmu.edu, junginny@andrew.cmu.edu
--*-IMPORTANT NOTE-*-------------------------------------
Though this final project is a collaboration between the two of us, we both wrote the code entirely together on my computer, because everything fits like a puzzle with everything
else and having it divided up among us would have made the code much less efficient (at least in our opinion). So, after meeting with Prof. Dannenberg, he agreed that as long
as this project is thoroughly documented, we can both be graded on the project in it's entirety.
--*-VISUAL DESCRIPTION-*---------------------------------
This project uses keyboard input to press the corresponding keys, which are shown when hovering over the area displayed in the top right corner of the canvas. When the key is
pressed, the corresponding note is played, a note with a color corresponding to the key and varying properties including fadeout rate, vertical speed, and note tail orientation
is created. The amount of notes created depends on how long the key is pressed down for. Lights on top of the speakers on each side of the stage and at the top display the
corresponding color, ending at the corresponding dancer location. The dancer at that location also cycles through its animation, and remembers what frame it was on and pauses when
the key is not pressed. There are four different types of dances, based on key position. Grey flickering lights, somewhat resembling a glowing disco ball (which is why its 
called discoLighting in the code) also randomly flicker both above the speakers and at the top behind the lights when a key is pressed.
--*-IMPROVEMENTS TO STRUCTURE/ FUTURE IMPLEMENTATIONS-*--
1-- A Dictionary called keyToColorHex was implemented using Hexcodes for colors near the start for the colors of each corresponding note, but later on in the project we wanted
    to vary the fadeout rate of the notes, and using fill(hexcode,alpha) is not possible, so we had to write a basic hexcode to r,g,b converter found between lines ~100 - ~120.
    These lines are unneccessary if the dictionary is changed to be in a similar format as the discoLighting array, with each key taking an r,g,b entry instead.
2-- THE SOUNDS LOOP WHEN THE KEY IS PRESSED, INSTEAD OF JUST PLAYING ONCE NO MATTER HOW LONG THE KEY IS HELD DOWN FOR ****THIS HAS BEEN FIXED AS OF 12-4-2018****
3-- The sound when the piano key is pressed should have the duration change up to a certain length while the key is held down, then fade out like a piano does in the real world
    However, when lengthening the sound using p5 sound commands, the pitch decreases (as the reference sheet warns), so a more complex solution would be required. Because the 
    main focus of this project shifted from the piano to the visual display as it was created, we feel this is not an urgent change, though it would improve the overall quality
    of the project.
--*-SOURCES FOR SOUNDS AND DANCE ANIMATIONS-*------------
This project uses free sprite sheets available for download at https://www.deviantart.com/gladkirby7/art/Vexy-Dances-FruityDance-Sprite-Sheet-675963897 for the arm dance and
windmill & https://www.pinterest.com/pin/525021269044190160/?lp=true for the spin dance && https://blueprintinteractive.com/sites/default/files/images/carlton_sprites2.png
for the man dance. The sounds for each piano key were downloaded from https://freesound.org/people/jobro/packs/2489/.
************************************************************************************************************************************************************************************/
// Variables corresponding to notes Example: cO1 = Note C in Octave 1
var cO1, dO1, eO1, fO1, gO1, aO1, bO1, cSO1, dSO1, fSO1, gSO1, aSO1, cO2, dO2, eO2, fO2, gO2, aO2, bO2, cSO2, dSO2, fSO2, gSO2, aSO2;
// Array that keeps track of total presses for each key Example: cO1 is index position 0, stores X amount of clicks
var numberOfPresses = [];
// Arrays used to store animation frames for their specific dance
var windMillDance = [];
var armDance = [];
var manDance = [];
var spinDance = [];
// Array used to store what the dancers can do Example: what dance it does, what frame of the dance it is on, if it is dancing or not
var peopleArray = [];
// Array used to store the specific property within the people Array Example: 0 means it is on the first frame, 7 means it is on the last frame
var whatFrameIsItArray = [];
// Array to store the notes with their specific properties when a key is pressed
var notesArray = [];
// Dictionary to store whether a key is already pressed down when the keys functions are called again.
var wasPressed = {'Q': false, '2': false, 'W': false, '3': false, 'E': false, 'R': false, '5': false, 'T': false, '6': false, 'Y': false, '7': false, 'U': false, 'Z': false, 'S': false, 'X': false, 'D': false, 'C': false, 'V': false, 'G': false, 'B': false, 'H': false, 'N': false, 'J': false, 'M': false};
// Variables to determine width and height of keys
var whiteBottomHeight = 15;
var wKeyW = 40;
var wKeyH = 190;
var blackBottomHeight = 10;
var bKeyW = 30;
var bKeyH = 135;
// Dictionaries used to convert keys to the corresponding information, documented in more detail when they are called in specific functions below
var keyToColorHex = {'Q': '#D8BFD8', '2': '#3C8696', 'W': '#2DD652', '3': '#B632A3', 'E': '#F9EF15', 'R': '#8A36F6', '5': '#D64939', 'T': '#2DEC74', '6': '#B08FD2', 'Y': '#25BFEC', '7': '#EC838D', 'U': '#534EEE', 'Z': '#B41E3C', 'S': '#ECC25F', 'X': '#1DB071', 'D': '#7F75F0', 'C': '#EE853E', 'V': '#CFE397', 'G': '#6EEEDE', 'B': '#EE6EC8', 'H': '#FB5441', 'N': '#7B90D8', 'J': '#EA1D32', 'M': '#91F473'}; 
var whiteKeyToX = {'Q': 20, 'W': 60, 'E': 100, 'R': 140, 'T': 180, 'Y': 220, 'U': 260, 'Z': 300, 'X': 340, 'C': 380, 'V': 420, 'B': 460, 'N': 500, 'M': 540};
var blackKeyToX = {'2': 45, '3': 85, '5': 165, '6': 205, '7': 245, 'S': 325, 'D': 365, 'G': 445, 'H': 485, 'J': 525};
var keyItoClicks = {'Q': 0, '2': 1, 'W': 2, '3': 3, 'E': 4, 'R': 5, '5': 6, 'T': 7, '6': 8, 'Y': 9, '7': 10, 'U': 11, 'Z': 12, 'S': 13, 'X': 14, 'D': 15, 'C': 16, 'V': 17, 'G': 18, 'B': 19, 'H': 20, 'N': 21, 'J': 22, 'M': 23};
// Array used for the respective shapes below and behind the lights to flicker "randomly" (in fact it is just shuffled) when the lights flicker, which make the lights prettier
var discoLighting = [(220,220,220),(211,211,211),(192,192,192),(169,169,169),(128,128,128),(105,105,105),(119,136,153),(112,128,144),(47,79,79),(47,79,79)];
// Preloading in all the sounds and animations for the dances, outputting to console when each respective group of files are ready to play
function preload() {
    cO1 = loadSound("cKeyOctave1.wav");
    dO1 = loadSound("dKeyOctave1.wav");
    eO1 = loadSound("eKeyOctave1.wav");
    fO1 = loadSound("fKeyOctave1.wav");
    gO1 = loadSound("gKeyOctave1.wav");
    aO1 = loadSound("aKeyOctave1.wav");
    bO1 = loadSound("bKeyOctave1.wav");
    console.log("Loaded White Keys Octave 1");
    cSO1 = loadSound("cSharpOctave1.wav");
    dSO1 = loadSound("dSharpOctave1.wav");
    fSO1 = loadSound("fSharpOctave1.wav");
    gSO1 = loadSound("gSharpOctave1.wav");
    aSO1 = loadSound("aSharpOctave1.wav");
    console.log("Loaded Black Keys Octave 1");
    cO2 = loadSound("cKeyOctave2.wav");
    dO2 = loadSound("dKeyOctave2.wav");
    eO2 = loadSound("eKeyOctave2.wav");
    fO2 = loadSound("fKeyOctave2.wav");
    gO2 = loadSound("gKeyOctave2.wav");
    aO2 = loadSound("aKeyOctave2.wav");
    bO2 = loadSound("bKeyOctave2.wav");
    console.log("Loaded White Keys Octave 2");
    cSO2 = loadSound("cSharpOctave2.wav");
    dSO2 = loadSound("dSharpOctave2.wav");
    fSO2 = loadSound("fSharpOctave2.wav");
    gSO2 = loadSound("gSharpOctave2.wav");
    aSO2 = loadSound("aSharpOctave2.wav");
    console.log("Loaded Black Keys Octave 2");
    for (var i = 0; i < 8; i++) {
        windMillDance[i] = loadImage("assets/windmill" + i + ".png");
        armDance[i] = loadImage("assets/Arm" + i + ".png");
        manDance[i] = loadImage("assets/man" + i +".png");
        spinDance[i] = loadImage("assets/spingirl" + i +".png")
    }
    console.log("Loaded Dance Animations");
}
/*************************************************************************************************************************************************************************************
Below are the functions used for this project. Documenting these is somewhat hard because many functions use parts of other functions, in addition to many arrays / dictionaries
*************************************************************************************************************************************************************************************/
// These functions remove the hashtag from the color hexcode, then converts the remaining hexcode into a base 16 number, from which the r,g,b values can be extracted based on
// their respective positions within the resulting number.
// This function converts the hexcode into the r value.
function hexToR(hex) {
    hex = hex.replace(/[^0-9A-F]/gi, '');
    var hexConvert = parseInt(hex,16);
    var r = (hexConvert >> 16) & 255;
    return r;
}
// This function converts the hexcode into the g value.
function hexToG(hex) {
    hex = hex.replace(/[^0-9A-F]/gi, '');
    var hexConvert = parseInt(hex,16);
    var g = (hexConvert >> 8) & 255;
    return g;
}
// This function converts the hexcode into the b value.
function hexToB(hex) {
    hex = hex.replace(/[^0-9A-F]/gi, '');
    var hexConvert = parseInt(hex,16);
    var b = hexConvert & 255;
    return b;
}
// This function's overall purpose is to take in properties it needs from various other parts, then draws a light from the top of the screen down to the corresponding character
// using the x property. It also draws the light in the color corresponding to the key pressed, coming from the colorHex, key, and whatDance properties. Another group of lights
// are also drawn above the speakers in the same color as the overhead lights.
function makeLight(x,colorHex,key,whatDance) {
    stroke(hexToR(keyToColorHex[key]),hexToG(keyToColorHex[key]),hexToB(keyToColorHex[key]),random(80,120));
    strokeWeight(1);
    fill(hexToR(keyToColorHex[key]),hexToG(keyToColorHex[key]),hexToB(keyToColorHex[key]),random(80,120));
//  This next section of code draws the overhead lights
    beginShape();
    var mappedX = map(x,20,540,220,382);
    vertex(mappedX - 2.5,50 + random(-2.5,2.5));
    vertex(mappedX + 2.5,50);
//  The if statements determine the end location of the light, based on what position the dancer is in and what dance it is doing.
    if (peopleArray[whatDance].framesArray == spinDance) {
        var endX = (peopleArray[whatDance].x - 25) * 0.85 + 70;
        var endY = peopleArray[whatDance].y + 20;
        var sizeBeam = 1;
    }
    else if (peopleArray[whatDance].framesArray == armDance) {
        var endX = peopleArray[whatDance].x * 0.7 + 120;
        var endY = peopleArray[whatDance].y - 50;
        var sizeBeam = 0.75;
    }
    else if (peopleArray[whatDance].framesArray == manDance) {
        var endX = peopleArray[whatDance].x * 0.5 + 165;
        var endY = peopleArray[whatDance].y - 90;
        var sizeBeam = 0.5;
    }
    else if (peopleArray[whatDance].framesArray == windMillDance) {
        var endX = peopleArray[whatDance].x * 1.1 + 50;
        var endY = peopleArray[whatDance].y + 100;
        var sizeBeam = 1.3;
    }
    vertex(endX + (25 * sizeBeam), endY);
    vertex(endX - (25 * sizeBeam), endY)
    endShape();
//  This for loop draws the lights in the same color as the overhead light on the tops of the side speakers
    for (var i = 0; i < 5; i++) {
        triangle(20 * i,197,(20 * i) - 13,0,(20 * i) + 15,0);
        triangle(20 * i + 15,194,(20 * i + 15) - 3,0,(20 * i + 15) + 8,0);
        triangle(20 * i + 510,197,(20 * i + 510) - 9,0,(20 * i + 510) + 15,0);
        triangle(20 * i + 495,194,(20 * i + 495) - 7,0,(20 * i + 495) + 13,0);
    }
    noStroke();
}
// This function loads people into an array of size 24, corresponding to the number of keys on the piano. It also loads in the animation frame array, setting each of the dancers
// to the first frame of their 8-frame animation.
function loadAnimationArray() {
    for (var i = 0; i < 24; i++) {
        peopleArray[i] = makePerson(22 * i + 5);
        whatFrameIsItArray[i] = 0;
    }
}
// This function is what creates and returns all the variables needed for the person to know what it needs to. This is the object that is loaded into the peopleArray.
function makePerson(x) {
    var person = {x: x, y: 245 + random(-3,3), framesArray: windMillDance, whatFrameIsIt: whatFrameIsItArray, startDancing: startDancing};
    return person;
}
// This function sets the dances for people in the array. When the array is first made, the frameArray property is set at a default windMillDance as shown above.
function setDancesForPeople() {
//  This for loop iterates through the length of the peopleArray to make sure every dancer's dance is changed accordingly.
    for (var i = 0; i < 24; i++) {
//      These if statements load in the corresponding dance based on the modulus operator Example: every fourth dancer does the spin dance.
        if (i % 4 == 0) {
            peopleArray[i].framesArray = windMillDance;
        }
        else if (i % 4 == 1) {
            peopleArray[i].framesArray = armDance;
        }
        else if (i % 4 == 2) {
            peopleArray[i].framesArray = manDance;
        }
        else if (i % 4 == 3) {
            peopleArray[i].framesArray = spinDance;
        }
    }
}
// This function is called startDancing, because it is the function that actually makes the image on the canvas, but the main purpose is to resize the images and put them into
// their corresponding place based on their dance, because each image was a different size initially.
function startDancing(whatFrame) {
    push();
//  These if statements are what resize the image as described above, and move it to their location based on the x and y properties (an additional hardcoded shift had to be 
//  implemented because of the rescaling of the image).
    if (this.framesArray == manDance) {
        translate(this.x * 0.45 + 160,this.y - 135);
        scale(0.25,0.25);
    }
    else if (this.framesArray == spinDance) {
        translate((this.x - 25) * 0.8 + 45,this.y - 60);
        scale(0.7,0.7);
    }
    else if (this.framesArray == windMillDance) {
        translate(this.x + 25,this.y);
        scale(0.3,0.2);
    }
    else if (this.framesArray == armDance) {
        translate(this.x * 0.7 + 100,this.y - 100);
        scale(0.15,0.1);
    }
    image(this.framesArray[whatFrameIsItArray[whatFrame]], 0, 0);
    pop();
}
// This function updates the dancers to the next frame of their animation, and also updates the numberOfPresses Array.
function checkAndUpdateDancing() {
//  This for loop calls the start dance function, shown above.
    for (var i = 0; i < 24; i++) {
        peopleArray[i].startDancing(i);
//      This if statement increases the number of presses every 5 "actual" presses, because I found the refresh rate to be too fast. Increasing the 5 will slow down the animation
//      Example: changing the 5 to a 10 will approximately halve the animation speed.
        if (numberOfPresses[i] % 5 == 0) {
            numberOfPresses[i]++;
            whatFrameIsItArray[i]++;
//          This if statement ensures the animations smoothly loop, and dont return undefined because each animation is only 8 frames long.
            if (whatFrameIsItArray[i] > 7) {
                whatFrameIsItArray[i] = 0;
            }
        }
    }
}
// This function is what creates and returns a note with all of the properties it needs to know.
function makeNote(x, colorHex) {
    var note = {x: random(5,550), y: 285 + random(-25,25), colorHex: colorHex, size: random(0.2,1), lineFactor: random(1.5,3.5), willItBeRotated: random(0,2), howFastItGoesUp: random(0.7,1.4), opacity: 255, fadeOutChange: random(0.4,3)};
    return note;
}
// This function is what actually displays the notes on the canvas, as well as updates existing notes in the notesArray based on their respective properties, and also updates the
// property that determines how fast it fades out.
function displayNotes() {
//  This function iterates through the notesArray.
    for (var i = 0; i < notesArray.length; i++) {
        strokeWeight(notesArray[i].size * 3.5);
        stroke(hexToR(notesArray[i].colorHex),hexToG(notesArray[i].colorHex),hexToB(notesArray[i].colorHex),notesArray[i].opacity);
        fill(hexToR(notesArray[i].colorHex),hexToG(notesArray[i].colorHex),hexToB(notesArray[i].colorHex),notesArray[i].opacity);
        ellipse(notesArray[i].x,notesArray[i].y,notesArray[i].size * 20,notesArray[i].size * 15);
//      This if statement determines if the note tail will be up or down.
        if (notesArray[i].willItBeRotated < 1) {
            line(notesArray[i].x + (notesArray[i].size * 10),notesArray[i].y,notesArray[i].x + (notesArray[i].size * 10),notesArray[i].y - (notesArray[i].size * 20 * notesArray[i].lineFactor));
            line(notesArray[i].x + (notesArray[i].size * 10),notesArray[i].y - (notesArray[i].size * 20 * notesArray[i].lineFactor),notesArray[i].x + (notesArray[i].size * 15),notesArray[i].y - (notesArray[i].size * 20 * notesArray[i].lineFactor) + (notesArray[i].lineFactor * notesArray[i].size * 10));
        } else {
            line(notesArray[i].x - (notesArray[i].size * 10),notesArray[i].y,notesArray[i].x - (notesArray[i].size * 10),notesArray[i].y + (notesArray[i].size * 20 * notesArray[i].lineFactor));
            line(notesArray[i].x - (notesArray[i].size * 10),notesArray[i].y + (notesArray[i].size * 20 * notesArray[i].lineFactor),notesArray[i].x - (notesArray[i].size * 15),notesArray[i].y + (notesArray[i].size * 20 * notesArray[i].lineFactor) - (notesArray[i].lineFactor * notesArray[i].size * 10));
        }
        notesArray[i].opacity -= notesArray[i].fadeOutChange;
    }
    strokeWeight(1);
}
// This function goes through the notesArray, which stores all the note objects and updates the y coordinate of each note object based on its howFastItGoesUp property. Thus, every
// note has a different speed at which it ascends, leading to a more pleasing visual.
function checkAndUpdateNotes() {
//  This for loop iterates through the notesArray.
    for (var i = 0; i < notesArray.length; i++) {
//      This if statement checks whether the y coordinate of each note needs to be updated.
        if (notesArray[i].y != 0) {
            notesArray[i].y -= notesArray[i].howFastItGoesUp;
        }
    }
}
// This function removes notes that have gone offscreen.
function removeOffScreenNotes() {
//  This for loop iterates through the notesArray.
    for (var i = 0; i < notesArray.length; i++) {
//      This if statement checks whether the vertical coordinate is less than or equal to 0, and if it is, it removes the note from the array. I thought about putting this together
//      with the above function, but if I did that, when the array length changes because of the resulting shift, it might cause some minor errors in the code, so it is safer to 
//      split them up.
        if (notesArray[i].y <= 0) {
            notesArray.shift(notesArray[i]);
        }
    }
}
// This function determines whether the note will come out when the key is pressed (keys are very sensitive and log presses rapidly when the key is held down).
function noteComesOut() {
    var probability = random(0,1);
//  Changing the value in this if statement will change the amount of notes on screen, if you want less clutter.
    if (probability >= 0.5) {
        return true;
    } else {
        return false;
    }
}
// This function is what displays the piano and the steps as well as the speakers at the tops and sides of the canvas. 
function pianoStructureAndLights() {
//  Creating the rectangles that make up the piano.
    rectMode(CORNER);
    noStroke();
    stroke('#8B4513');
    fill('#3f2a14');
    rect(0,397,600,205);
    noStroke();
    fill('#52361b');
    rect(0,360,600,37);
    stroke('#D4AF37');
    fill('#8b5d2e');
    rect(0,260,600,100);
    fill('#2F4F4F');
    stroke('#2F4F4F');
    line(300,0,300,15)
    line(220,15,382,15);
//  This for loop creates the changing greyscale shapes on top of the speakers and behind the overstage lights, as well as the overhead light structure.
    for (var i = 0; i < discoLighting.length; i++) {
        stroke('#2F4F4F');
        line(220 + 18 * i,15,220 + 18 * i,35);
        fill(20);
        rectMode(CENTER);
        rect(220 + 18 * i,35,15,30);
        fill(discoLighting[i]);
        ellipse(220 + 18 * i,50,15,5);
        noStroke();
        fill(discoLighting[i]);
//      This if statement handles the left speaker.
        if (i < 5) {
            rect(20 * i,197,18,7);
            rect(20 * i + 7.5,196,18,7);
            rect(20 * i + 15,194,18,7);
        }
//      This if statement handles the right speaker.
        if (i >= 5) {
            rect(20 * (i - 5) + 510,197,18,7);
            rect(20 * (i - 5) + 502.5,196,18,7);
            rect(20 * (i - 5) + 495,194,18,7);
        }
//      Creates the actual speaker structures on the side speakers.
        rectMode(CORNER);
        stroke('#2F4F4F');
        strokeWeight(2);
        fill(90);
        rect(0,200,81,100);
        quad(82,200,103,193,103,300,82,300);
        rect(509,200,91,100);
        quad(509,200,487,193,487,300,509,300);
        fill('#778899');
        ellipse(32,230,45,45);
        ellipse(559,230,45,45);
        ellipse(32,280,25,25)
        ellipse(559,280,25,25);
        noStroke();
        fill(230);
        ellipse(32,230,5,5);
        ellipse(32,280,3,3);
        ellipse(559,230,5,5);
        ellipse(559,280,5,5);
        strokeWeight(1);
    }
//  Creates the steps the dancers are on.
    rectMode(CORNER);
    stroke('#D4AF37');
    fill('#52361b');
    rect(35,270,530,50);
    rect(95,200,400,40);
    rect(145,160,300,20);
    fill('#8b5d2e');
    quad(35,270,95,240,495,240,565,270);
    quad(95,200,145,180,445,180,495,200);
    quad(145,160,445,160,405,150,185,150); 
}
// This function shuffles an array's contents randomly, and is used to shuffle the greyscale shapes behind the lights for the discoLighting array.
function shuffler(array) {
    var currentPos = array.length, tempValue, randomPos;
    while (currentPos !== 0) {
        randomPos = Math.floor(Math.random() * currentPos);
        currentPos --;
        tempValue = array[currentPos];
        array[currentPos] = array[randomPos];
        array[randomPos] = tempValue;
    }
    return array;
}
// This function displays the white keys on the canvas, and alot of other things documented within the function.
function whiteKeys() {
    rectMode(CORNER);
//  Iterates through the keys found in the whiteKey dictionary, and assigns the corresponding position to the x variable.
    for (var key in whiteKeyToX){
        var x = whiteKeyToX[key];
//      Does alot of stuff when the key is pressed.
        if (keyIsDown(key.charCodeAt(0))){
//          Increases the number of presses of the particular key within the numberOfPresses Array.
            var particularClick = keyItoClicks[key];
            numberOfPresses[particularClick] += 1;
//          Displays the key on the canvas
            fill(255);
            stroke(0);
            rect(x,410,wKeyW,wKeyH);
//          Dictionary to associate the sound variables defined at the beginning with the corresponding key.
            var whiteKeyToSound = {'Q': cO1, 'W': dO1, 'E': eO1, 'R': fO1, 'T': gO1, 'Y': aO1, 'U': bO1, 'Z': cO2, 'X': dO2, 'C': eO2, 'V': fO2, 'B': gO2, 'N': aO2, 'M': bO2};
            var keySound = whiteKeyToSound[key];
//          Plays the actual sound without looping.
            if (!wasPressed[key]) {
                keySound.play();
                wasPressed[key] = true;
            }
//          To reduce clutter on the screen, placed the visuals within the noteComesOut function.
            if (noteComesOut()) {
//              Creates a note object in the notesArray.
                notesArray.push(makeNote(x,keyToColorHex[key]));
//              Makes a light based on the parameters defined within this function.
                makeLight(x,keyToColorHex[key],key,particularClick);
//              Shuffles the discoLighting array everytime a note comes out.
                discoLighting = shuffler(discoLighting);
//              Makes the larger circle at the top of each speaker pulse each time a note comes out.
                strokeWeight(3);
                fill('#778899');
                stroke('#2F4F4F');
                var pulse = random(-5,5);
                ellipse(32,230,50 + pulse,50 + pulse);
                ellipse(559,230,50 + pulse,50 + pulse);
                strokeWeight(1);
                fill(230);
                noStroke();
                ellipse(32,230,5,5);
                ellipse(559,230,5,5);
            }
//      This draws the key in the normal, unpressed position and lets you play the sound again the next time you press the key down.
        } else {
            wasPressed[key] = false;
            fill(255);
            stroke(0);
            rect(x,410,wKeyW,wKeyH - whiteBottomHeight);
            fill(150);
            rect(x,410 + wKeyH - whiteBottomHeight, wKeyW, whiteBottomHeight);
        }
    }
}
// This function displays the black keys on the canvas, and alot of other things documented within the function.
function blackKeys() {
    rectMode(CORNER);
//  Iterates through the keys found in the blackKey dictionary, and assigns the corresponding position to the x variable.
    for (var key in blackKeyToX){
        var x = blackKeyToX[key];
//      Does alot of stuff when the key is pressed.
        if (keyIsDown(key.charCodeAt(0))){
//          Increases the number of presses of the particular key within the numberOfPresses Array.
            var particularClick = keyItoClicks[key];
            numberOfPresses[particularClick] += 1;
//          Displays the key on the canvas
            fill(60);
            stroke(60);
            rect(x,400,bKeyW,bKeyH);
//          Dictionary to associate the sound variables defined at the beginning with the corresponding key.
            var blackKeyToSound = {'2': cSO1, '3': dSO1, '5': fSO1, '6': gSO1, '7': aSO1, 'S': cSO2, 'D': dSO2, 'G': fSO2, 'H': gSO2, 'J': aSO2};
            var keySound = blackKeyToSound[key];
//          Plays the actual sound without looping.
            if (!wasPressed[key]) {
                keySound.play();
                wasPressed[key] = true;
            }
//          To reduce clutter on the screen, placed the visuals within the noteComesOut function.
            if (noteComesOut()) {
//              Creates a note object in the notesArray.
                notesArray.push(makeNote(x,keyToColorHex[key]));
//              Makes a light based on the parameters defined within this function.
                makeLight(x,keyToColorHex[key],key,particularClick);
//              Shuffles the discoLighting array everytime a note comes out.
                discoLighting = shuffler(discoLighting);
//              Makes the larger circle at the top of each speaker pulse each time a note comes out.
                strokeWeight(3);
                stroke('#2F4F4F');
                fill('#778899');
                var pulse = random(-5,5);
                ellipse(32,230,50 + pulse,50 + pulse);
                ellipse(559,230,50 + pulse,50 + pulse);
                fill(230);
                strokeWeight(1);
                noStroke();
                ellipse(32,230,5,5);
                ellipse(559,230,5,5);
            }
//      This draws the key in the normal, unpressed position and lets you play the sound again the next time you press the key down.
        } else {
            wasPressed[key] = false;
            fill(60);
            stroke(60);
            rect(x,400,bKeyW,bKeyH - blackBottomHeight);
            fill(30);
            stroke(30);
            rect(x,400 + bKeyH - blackBottomHeight, bKeyW, blackBottomHeight);
        }
    }
}
// This function shows what keyboard key corresponds to what piano key.
function showControls() {
    textAlign(CENTER,CENTER);
    textSize(20);
//  White keys are displayed with black text.
    fill(0);
    text("Q",40,570);
    text("W",80,570);
    text("E",120,570);
    text("R",160,570);
    text("T",200,570);
    text("Y",240,570);
    text("U",280,570);
    text("Z",320,570);
    text("X",360,570);
    text("C",400,570);
    text("V",440,570);
    text("B",480,570);
    text("N",520,570);
    text("M",560,570);
//  Black keys are displayed with white text.
    fill(255);
    text("2",60,520);
    text("3",100,520);
    text("5",180,520);
    text("6",220,520);
    text("7",260,520);
    text("S",340,520);
    text("D",380,520);
    text("G",460,520);
    text("H",500,520);
    text("J",540,520);
}
// This function is just for clarity and structure.
function piano() {
    whiteKeys();
    blackKeys();
}

function setup() {
    createCanvas(600,602);
//  Loading the numberOfPresses Array to track amount of presses of each key.
    for (var i = 0; i < 24; i++) {
        numberOfPresses[i] = 0;
    }
//  Sets up arrays that need to be present before draw is called.
    loadAnimationArray(); 
    setDancesForPeople();
}

function draw() {
    background(30);
//  Calling corresponding functions documented above.
    pianoStructureAndLights();
    piano();
    checkAndUpdateDancing();
    displayNotes();
    checkAndUpdateNotes();
    removeOffScreenNotes();
//  Visually representing and calling the function to display controls if mouse is hovered over it.
    stroke(220);
    fill(100);
    rect(490,0,120,20);
    textSize(12);
    textAlign(LEFT,BOTTOM);
    stroke(180);
    fill(180);
    text("Hover For Controls",495,17);
    if (mouseX >= 490 & mouseY <= 20) {
        showControls();
    }
}

To run this project, please download the zip file located below, and then create a local server so that the sounds can be loaded properly.

104 final project zip file

This project uses keyboard input to press the corresponding keys, which are shown when hovering over the area displayed in the top right corner of the canvas. When the key is pressed, the corresponding note is played, a note with a color corresponding to the key and varying properties including fadeout rate, vertical speed, and note tail orientation is created. The amount of notes created depends on how long the key is pressed down for. Lights on top of the speakers on each side of the stage and at the top display the
corresponding color, ending at the corresponding dancer location. The dancer at that location also cycles through its animation, and remembers what frame it was on and pauses when the key is not pressed. There are four different types of dances, based on key position. Grey flickering lights, somewhat resembling a glowing disco ball (which is why its called discoLighting in the code) also randomly flicker both above the speakers and at the top behind the lights when a key is pressed.

This project uses free sprite sheets available for download at: https://www.deviantart.com/gladkirby7/art/Vexy-Dances-FruityDance-Sprite-Sheet-675963897 for the arm dance and windmill dance. https://www.pinterest.com/pin/525021269044190160/?lp=true for the spin dance.

https://blueprintinteractive.com/sites/default/files/images/carlton_sprites2.png for the man dance.

The sounds for each piano key were downloaded from https://freesound.org/people/jobro/packs/2489/.

We learned a lot about code structure when making this, and also about how useful dictionaries are for converting information in an organized way. Overall we are pretty pleased with the project and how it can handle many keys being pressed without slowing down at all.

Please see the note in the comments at the top of the code for how we would like to be graded on this project.

Kevin Riordan Sharon Yang-Project 12-Proposal

We are using one of the grace days on this assignment.

Our final project proposal is making a program that simulates a piano, like the apps available on iPhones. When keys are pressed, sound comes out, and maybe a note floats up to the top out of the key. The size of the notes or movement could be based on how long the key is pressed for or something. Creating the visual of pressing down the key and making the piano look realistic will be challenging, and making the sounds realistic and reactive will also be something we have to figure out. We have not really done much with sound in this class, so working more with sound will be challenging. Below are some sketches of what we plan to try and make visually. The basic program to make it work seems to be something realistic to try and work towards, but making it visually more appealing is something we want to work on more.

what the interface might look like

I will be working with Sharon Yang (also in Section C) on this project.

Kevin Riordan Looking Outwards-11-Section C

For this week’s looking outwards, the algorithmic music project that I chose is Andrius Sarpovas’ Kinetic Generative Music Installation. Through transposition and conversion, the data of half a million people is turned into energy impulses that produce a complex acoustic variation. I admire his creativity of thinking of turning statistics into a musical composition. I resonate with his belief that music is not merely songs that we usually listen to but it is also the rhythmic patterns, the space, the melody or the harmony that we encounter in our daily life. I think that turning different people’s consciousness into music is meaningful because individual’s own presence is represented in a very unique way. The project consists of metal, wood, plastic and glass which are the sources of the sounds. Aluminium bars are used to create a lasting and rich harmony. The segments of the installation were hung on wires through which the impulses were transferred to the sound activator and the damper. The algorithm of transposition and conversion turned statistics into impulses which were distributed to the segments to make sounds. These sounds created are rather relaxing, which has been achieved through the artist’s work on the the algorithm and the mechatronics. This shows that the artist sends a message to his audience that technology can not only be used to create solitude for people but also make the solitude comforting.

Source: https://metalmagazine.eu/en/post/interview/andrius-sarapovas-transforming-data-into-music

installation sound – 26 / 77 segments recorded – 17.06.25 from Andrius Šarapovas on Vimeo.

Kevin Riordan Project 11- Composition

kzr turtle project

/*Kevin Riordan
Section C
kzr@andrew.cmu.edu
project_11*/
//details 1 changes how detailed it is horizontally, width gives pixel perfect color
//details 2 changes how detailed it is vertically, 1 gives pixel perect change
var details1 = 65;
var details2 = 5;
function preload() {//i have 2 background images, farnham jahanian or subra suresh
    var myImageLink = "https://i.imgur.com/PhOrH0d.jpg";
    //var myImageLink = "https://i.imgur.com/WNxbkQj.jpg";
    myImage = loadImage(myImageLink);
}

function setup() {
    createCanvas(480,480);
    background(0);
    myImage.loadPixels();//loading pixels in to use get later
}

function draw() {
    var t = makeTurtle(0,0);//make my turtle 🙂
    t.doTheStuff(width / details1,details2);//watch it do the stuff 🙂
}
//colorChangeDist is how often it changes color, is equal to details1
function doTheStuff(colorChangeDist,rowSize){
    var pixelColor;
    this.setWeight(details2);
    var increment = colorChangeDist;
    var numRows = height / rowSize;
    for(var k = 0; k < numRows; k++){//for loop to iterate vertically
        for(var i = 0; i < width; i += increment){//for loop for the odd rows
            pixelColor = myImage.get(this.x,this.y);
            this.setColor(color(pixelColor));
            this.forward(increment);
        }
        this.right(90);
        this.forward(rowSize);
        this.right(90);//turns it back after moving it to the next row position
        for(var j = width; j > 0; j -= increment){//for loop for the even rows
            pixelColor = myImage.get(this.x,this.y);
            this.setColor(color(pixelColor));
            this.forward(increment);
        }
        this.left(90);
        this.forward(rowSize);
        this.left(90);//turns it back after moving it to the next row position
    }
}
//turtle stuff
function turtleLeft(d) {
    this.angle -= d;
}
 
 
function turtleRight(d) {
    this.angle += d;
}
 
 
function turtleForward(p) {
    var rad = radians(this.angle);
    var newx = this.x + cos(rad) * p;
    var newy = this.y + sin(rad) * p;
    this.goto(newx, newy);
}
 
 
function turtleBack(p) {
    this.forward(-p);
}
 
 
function turtlePenDown() {
    this.penIsDown = true;
}
 
 
function turtlePenUp() {
    this.penIsDown = false;
}
 
 
function turtleGoTo(x, y) {
    if (this.penIsDown) {
      stroke(this.color);
      strokeWeight(this.weight);
      line(this.x, this.y, x, y);
    }
    this.x = x;
    this.y = y;
}
 
 
function turtleDistTo(x, y) {
    return sqrt(sq(this.x - x) + sq(this.y - y));
}
 
 
function turtleAngleTo(x, y) {
    var absAngle = degrees(atan2(y - this.y, x - this.x));
    var angle = ((absAngle - this.angle) + 360) % 360.0;
    return angle;
}
 
 
function turtleTurnToward(x, y, d) {
    var angle = this.angleTo(x, y);
    if (angle < 180) {
        this.angle += d;
    } else {
        this.angle -= d;
    }
}
 
 
function turtleSetColor(c) {
    this.color = c;
}
 
 
function turtleSetWeight(w) {
    this.weight = w;
}
 
 
function turtleFace(angle) {
    this.angle = angle;
}
 
 
function makeTurtle(tx, ty) {
    var turtle = {x: tx, y: ty,
                  angle: 0.0, 
                  penIsDown: true,
                  color: color(128),
                  weight: 1,
                  left: turtleLeft, right: turtleRight,
                  forward: turtleForward, back: turtleBack,
                  penDown: turtlePenDown, penUp: turtlePenUp,
                  goto: turtleGoTo, angleto: turtleAngleTo,
                  turnToward: turtleTurnToward,
                  distanceTo: turtleDistTo, angleTo: turtleAngleTo,
                  setColor: turtleSetColor, setWeight: turtleSetWeight,
                  face: turtleFace, doTheStuff: doTheStuff};//added a function about doing stuff
    return turtle;
}

I wanted to make the turtle draw out an underlying image for my project. By making two variables for how detailed I wanted the drawing to be, detail1 and detail2, I can control how clear or blurry the picture is. The images look really cool at a medium amount of detail level, and I like the way farnham and subra look when drawn by turtles. Overall, this project helped me better understand how functions work together and I had alot of fun with it.

Farnham at lowish amount of detail
Subra at medium amount of detail

Kevin Riordan Looking Outwards-10-Section C

For this week’s looking outwards, I chose Distortion Pavilion, a reconfigurable art piece by Caitlin Morris. This was created in 2010 for an annual club-culture music festival in Copenhagen, Denmark. The piece has been developed in a collaboration between American and Danish architects and designers. It was primarily constructed of acoustical foam. I admire how it is able to be both functional and aesthetic, as it is able to block sound  in the midst of the festival while being colorful and architecturally aesthetic to add to the festivity. I also admire that the acoustical foam is robust enough to have many people sit on it. The piece can be taken apart and reconfigured. The artist Caitlin Morris is based in New York and she is a designer and an engineer working with Hypersonic. She mainly focuses on digital and fabricated media. She explores the interaction between perceptions in physical space, with particular emphasis on sound and visuals. She also experiments with the boundary between digital and physical representations of space.

Interior of the Pavillion
distortion
A possible configuration

Kevin Riordan Project-10-Landscape

kzr sketch 10

/*Kevin Riordan
Section C
kzr@andrew.cmu.edu
project_10*/
var trees = [];//make empty trees array

function setup() {
    createCanvas(480, 480); 
    for (var i = 0; i < 6; i++){
        var randX = random(-width/5,width + (width / 3));//putting in 6 trees at the start
        trees[i] = makeTree(randX);
    }
    frameRate(10);//setting framerate
}

function draw() {
    background('#D8BFD8'); //setting background color
    makeBackestMountains();//furthest mountains
    makeBackMountains();//next furthest mountains
    updateAndDisplayTrees();//moving and displaying trees
    removeTreesThatHavePassed();//taking out trees that are safely offscreen
    addNewTreesProb();//putting in new trees sometimes
    makeFrontMountains();//make foreground
}
//updates and displays trees as they move to the left
function updateAndDisplayTrees(){
    for (var i = 0; i < trees.length; i++){
        trees[i].move();
        trees[i].display();
    }
}
//takes trees out of array once they are safely off screen
function removeTreesThatHavePassed(){
    var treesKept = [];
    for (var i = 0; i < trees.length; i++){
        if (trees[i].x > -width / 3) {
            treesKept.push(trees[i]);
        }
    }
    trees = treesKept;
}
//adds new trees with set chance
function addNewTreesProb() {
    var newTreeChance = 0.02; 
    if (random(0,1) < newTreeChance) {
        trees.push(makeTree(width + (width / 3)));
    }
}
//moves trees at 2 pixels per call
function treeMove() {
    this.x += this.speed;
}
//makes trees with specified things
function makeTree(xCoord) {
    var tree = {x: xCoord, speed: -2.0, move: treeMove, display: treeDisplay, size: random(0.5,1.3), grad: round(random(0,3))}
    return tree;
}
//draws tree recursively with fractals
function drawTree(x, y, angle, depth, size){
    if(depth != 0){
        var x2 = x + (Math.cos(angle) * 10 * size * depth);
        var y2 = y + (Math.sin(angle) * 15 * size * depth);
        line(x, y, x2, y2);
        drawTree(x2, y2, angle - (PI / 8), depth - 1, size);//left side of tree
        drawTree(x2, y2, angle + (PI / 8), depth - 1, size);//right side of tree
    }
}
//sets color based on the grad, 4 different colors possible
function treeDisplay(){
    var treeColor;
    if(this.grad == 0) {
        stroke('#FFF8DC');
    }
    if(this.grad == 1) {
        stroke('#FFEBCD');
    }
    if(this.grad == 2) {
        stroke('#FFE4C4');
    }
    if(this.grad == 3) {
        stroke('#FFDEAD');
    }
    strokeWeight(2);
    drawTree(this.x, height-30, -PI / 2, 6, this.size);//draws actual tree with fifth parameter changing the overall scale of it
}
//makes foreground with given detail, moves at same speed as trees
function makeFrontMountains() {
    var terrainSpeed1 = 0.00005;
    var terrainDetail1 = 0.005;
    stroke('#DAA520'); 
    beginShape(); 
    for (var x = 0; x < width; x++) {
        var t = (x * terrainDetail1) + (millis() * terrainSpeed1);
        var y1 = map(noise(t), 0,1, 400, height-30);
        line(x,y1,x,height); 
    }
    endShape();
}
//makes mountains in background, moves slower than trees and foreground
function makeBackMountains() {
    var terrainSpeed2 = 0.000025;
    var terrainDetail2 = 0.005;
    stroke('#A0522D'); 
    beginShape(); 
    for (var x2 = 0; x2 < width; x2++) {
        var u = (x2 * terrainDetail2) + (millis() * terrainSpeed2);
        var y2 = map(noise(u), 0,1, 150, height - 30);
        line(x2,y2,x2,height); 
    }
    endShape();
}
//makes furthest mountains, moves very slowly
function makeBackestMountains() {
    var terrainSpeed3 = 0.0000125;
    var terrainDetail3 = 0.01;
    stroke('#BC8F8F'); 
    beginShape(); 
    for (var x3 = 0; x3 < width; x3++) {
        var v = (x3 * terrainDetail3) + (millis() * terrainSpeed3);
        var y3 = map(noise(v), 0,1, 110, height - 150);
        line(x3,y3,x3,height); 
    }
    endShape();
}

I found the template for this project very helpful, but the part of this project that took me the most time was figuring out how to make the trees look more realistic. I used recursion and fractals to make the trees look nice, and realizing that I could call the same function inside of itself took a while for me to figure out. I also played around with speeds to make different things move at different speeds. I also found a website that gives color names for different colors, so I like the way my colors fit together for this project. This project made me really comfortable with functions and how they work together. I made the project look pretty close to how I wanted it to look which I am proud of.

sketch for project 10 landscape

Kevin Riordan Looking Outwards-09 Section C

Video Demonstration of the Three Desserts

I found Sharon’s Looking Outwards 04 post very interesting. She talked about how the creator wanted to add a new dimension to food and meals. I found the most interesting of the three projects to be the edible robotics embedded in a dessert, and think this has the most applications. I feel that the project focused too much on tying art into it, and I feel this could have valuable practical applications. For example, the idea of edible robotics is something that should definitely be explored further. Though this idea is very interesting, I think that further projects into this area could do much more imaginative and visually cooler stuff than this. I agree with Sharon that tying food and art together in this way is very cool though.

Sharon’s Post: https://courses.ideate.cmu.edu/15-104/f2018/2018/09/20/sharon-yang-looking-outwards-04/

Kevin Riordan Project-09-Portrait

kzr project 09

/*Kevin Riordan
Section C
kzr@andrew.cmu.edu
project_09*/
var underlyingImage;
//making count variable, for end point and for switching point
var count = 0;

function preload() {
    var myImageLink = "https://i.imgur.com/UPry60m.jpg";
    myImage = loadImage(myImageLink);
}

function setup() {
    noStroke();
    createCanvas(480, 480);
    background(0);
    myImage.loadPixels();
}

function draw() {
    var posX = random(width);
    var posY = random(height);
    rectMode(CENTER);
    //front half uses createRect function
    if(count <= 5000) {
        var countMap1 = map(count,0,5000,100,20);
        createRect(int(posX),int(posY),countMap1 / 10);
        count++;
    }
    //back half uses detailedRect function
    else if(count <= 10000) {
        createDetailRect(posX,posY,2);
        count++;
    }
}
//making larger crosses at the beginning
function createRect(initialX, initialY, size) {
    var iX = constrain(floor(initialX), 0, width - 1);
    var iY = constrain(floor(initialY), 0, height - 1);
    var pixelColor = myImage.get(iX, iY);
    fill(pixelColor);
    for(var i = size; i > 0; i --) {
        var cCoord = map(i,0,size,size * 2,0);
        rect(initialX + cCoord,initialY,i,i);
        rect(initialX - cCoord,initialY,i,i);
        rect(initialX,initialY - cCoord,i,i);
        rect(initialX,initialY + cCoord,i,i);
    }
}
//making detailed shapes for the end
function createDetailRect(initialX, initialY, size) {
    var iX = constrain(floor(initialX), 0, width-1);
    var iY = constrain(floor(initialY), 0, height-1);
    var pixelColor = myImage.get(iX, iY);
    fill(pixelColor);
    for(var change = 0; change <= 12; change ++) {
        var side = map(change,0,12,1,0);
        rect(initialX + (change / 2),initialY,size * side,size * side);
        rect(initialX,initialY + (change / 2),size * side,size * side);
        rect(initialX + (change / 2),initialY + (change / 2),size * side,size * side);
        rect(initialX - (change / 2),initialY,size * side,size * side);
        rect(initialX,initialY - (change / 2),size * side,size * side);
        rect(initialX - (change / 2),initialY - (change / 2),size * side,size * side);
        rect(initialX + (change / 2),initialY - (change / 2),size * side,size * side);
        rect(initialX - (change / 2),initialY + (change / 2),size * side,size * side);
    }
}

I started this project by making a function based on the example code in the project description. It was pretty cool modifying the code and seeing what shapes appeared, and I played around with making an overall count variable that caused the size to decrease, increasing the detail. If I had more time, I would have played around with constraining the detailing after the count was above 5000, to constrain it to areas that had not been filled in. Overall, though, I am pretty satisfied with this project.

picture at beginning (count <2500)
picture during middle (count = 5000)
picture at end (count = 10000)
reference picture i used for underlying image

Kevin Riordan Looking Outwards-08

INST-INT 2013 – Jared Ficklin from Eyeo Festival on Vimeo.

I chose to do this week’s Looking Outwards on Jared Ficklin. He is based in Austin, Texas, and he is currently a partner and Lead Creative Technologist at ArgoDesign. In his past, he worked at frog, becoming one of four frog fellows for his work. He is most interested in the interactions between user-experience and touch and multi-touch, and using physics to enhance the user’s experience. I admire the way he approaches his work, and his motto which is “Think by making, Deliver by demo”. He believes that the best way to integrate cutting edge technology is by using user experience simulation as early in the process as possible, which is something I admire.

In his presentation at INST-INT 2013, he talked about organizing frog party, which is the unofficial name for the opening to the SXSW interactive. I enjoyed his presentation style of making little jokes throughout to keep the audience engaged, while still making sure the important information was included. His slide design was also very nice, which is something I will be incorporating into my own presentations. He focused on the user experience entirely, not getting into the technical stuff at all, which was interesting to me.

His bio can be found at http://www.argodesign.com/jared-ficklin-bio.html

Kevin Riordan Project-07-Curves

kzr project 07

/*Kevin Riordan
Section C
kzr@andrew.cmu.edu
project_07*/
function setup() {
    createCanvas(480,480);
}
//this function checks whether a point is close enough to the step value to be drawn
function latticeCloseEnough(xc, yc, c1X, c1Y, c2X, c2Y, step, bSquare) {
    //define two variables that the function will return for the if case in draw
    var smaller = false;
    var bigger = false;
    //checking four corners of the "rectangle" i have defined around the point xc, yc
    for(var x = xc - step/2; x <= xc + step/2; x += step){
        for(var y = yc - step/2; y <= yc + step/2; y += step){
            //distance used to check
            r1r2 = dist(x,y,c1X,c1Y) * dist(x,y,c2X,c2Y)
            if (r1r2 <= bSquare) smaller = true;
            else bigger = true;
        }
    }
    return smaller & bigger;
}
//this is my curve function for cassiniOvals on mathworld
function cassiniOvals(c1X, c1Y, c2X, c2Y, colorStretch) {
    //this step variable determines how defined the latticegrid is, higher numbers make it very defined but lag the program badly
    var step = 8;
    //formula for this curve is dist1 * dist 2 = b^2
    var dist1 = dist(c1X, c1Y, mouseX, mouseY);
    var dist2 = dist(c2X, c2Y, mouseX, mouseY);
    var bSquare = dist1 * dist2;
    //these drew intermediate points used to test code
    //point(c1X,c1Y);point(c2X,c2Y);
    //these variables determine how wide and how many curves are drawn around the center points for each oval function
    var range = 3;
    var scale = 0.2;
    var start = 4;
    //converts bsquare into an array
    var bSquareArray = Array.apply(null, Array(range)).map(function (_, i) {return bSquare * scale * (i + start);});
    //drawing the ovals by checking across each point on the canvas, as determined by the step variable
    for (var y = 0; y <= height; y += step) {
        for (var x = 0; x <= width; x += step) {
            for (var pos = 0; pos < bSquareArray.length; pos ++) {
                var posColor = map(pos,0,range - 1,0,255);
                //changing the color for each size of oval in each cassiniOval
                stroke((posColor + 50) * colorStretch,(posColor - 50) * colorStretch,posColor * colorStretch);
                //calling the latticechecking function to see if a point should be drawn
                if(latticeCloseEnough(x, y, c1X, c1Y, c2X, c2Y, step, bSquareArray[pos])){
                    point(x,y);
                }
            }
        }
    }
}

function draw() {
    background(0);
    strokeWeight(2.5);
    //left center coordinates
    var c1X = width / 2;
    var c1Y = height / 2;
    //started by having two variables c1x and c2x when testing one cassiniOval, but both centers ended up only depending on c1x
    //var c2X = 7*width/8;
    var c2Y = height / 2;
    var amount = 4;
    //drawing the cassiniOVals
    for (var i = 1; i <= 2; i += (1 / amount)) {
        //declaring variable to change the color for each complete cassiniOval function
        var colorStretch = map(i,0,2,0.1,1);
        cassiniOvals(c1X * i,c1Y,c1X * (2 - i),c2Y, colorStretch);
    }
}

This project was really cool to me. I used the Cassini Oval curve for this project from the MathWorld site. The main part of this project for me was creating a lattice checking function, so that the points did not get wider apart as the distances from the centers increased for larger increments. When I used a basic distance checking function, when the b-squared distance increased, the “bands” of points got thicker. I also used the .apply function I found online, which was hard for me to learn at first because I did not know about lambda expressions.  Overall, this project really forced me to learn how to reorganize my code and the structure overall.

Below is a screenshot on the right of a standard overlapping Cassini ovals when distance is short, and another screenshot on the left when the mouse is towards one side of the canvas, making the distances from the two centers larger.