https://editor.p5js.org/tinaaayu/sketches/k4UdL52f_

thoughts

let meowSound;
let rowHeight;
let numRows = 5;
let pitches = [1, 1.5, 2, 2.5, 3]; // playback rates for the scale
let catImages = []; // array to hold cat images
let singingCats = []; // array to track which cats are singing

//load audio & images
function preload() {
  meowSound = loadSound("meow1.mp3");

  for (let i = 0; i < numRows; i++) {
    let normal = loadImage("cat_normal.jpg");
    let singing = loadImage("cat_singing.jpg");
    catImages.push({ normal, singing });
  }
}

//create canvas & rows
function setup() {
  createCanvas(windowWidth, windowHeight);
  rowHeight = height / numRows;

  // initialize singing state
  for (let i = 0; i < numRows; i++) {
    singingCats[i] = false;
  }

}

function draw() {
  background(255);

  // draw rows and cat visuals
  for (let i = 0; i < numRows; i++) {
    let y = i * rowHeight;

    // highlight row on hover
    if (mouseY > y && mouseY < y + rowHeight) {
      fill(200, 230, 255); // light blue for hover
    } else {
      fill(255); // white for non-hover
    }
    rect(0, y, width, rowHeight);

    //boolean - match state to image
    let img = singingCats[i] ? catImages[i].singing : catImages[i].normal;
    imageMode(CENTER);
    image(img, width / 4, y + rowHeight / 2, windowWidth/4, windowHeight/5);

    // speech bubble
    if (singingCats[i]) {
      fill(255, 255, 200);
      stroke(0);
      strokeWeight(2);
      ellipse(width / 2, y + rowHeight / 2 - 20, 80, 40); 
      fill(0);
      noStroke();
      textSize(14);
      textAlign(CENTER, CENTER);
      text("meow~", width / 2, y + rowHeight / 2 - 20);
    }

    fill(0);
    textSize(16);
    textAlign(CENTER, CENTER);
    text(`Pitch ${i + 1}`, 3 * width / 4, y + rowHeight / 2);
  }
}

//is mouse hovering
function mouseMoved() {
  for (let i = 0; i < numRows; i++) {
    let y = i * rowHeight;

    //if yes
    if (mouseY > y && mouseY < y + rowHeight) {
      if (!meowSound.isPlaying()) {
        meowSound.rate(pitches[i]);
        meowSound.play();
        singingCats[i] = true;
      }
    } else {
      
      //if no
      if (meowSound.isPlaying() && meowSound.rate() === pitches[i]) {
        meowSound.stop();
      }
      singingCats[i] = false;
    }
  }
}

global variables

  1. meowSound: holds the sound file for the meow
  2. rowHeight: determines the height of each row, dynamically calculated based on the number of rows and the canvas height
  3. numRows: number of rows in the application (set to 5)
  4. pitches: array of playback rates for the sound. each pitch corresponds to a row and alters the playback speed of the sound
  5. catImages: array holding normal and "singing" cat images for each row
  6. singingCats: boolean array tracking which cats are "singing" (true if the cat is singing, false otherwise)

function definitions

preload