Draggable Images Triggering and Processing Sound

I am trying to figure out a way to do a fairly simple idea: I have two PNGs which are draggable across the canvas and I want each of them to trigger a loop. Beyond that, I want the position of the image to dictate the pan() and rate() of the looping audio file.

I broke this down into smaller sketches, as ya do, and I have been able to get draggable images using found code, and I have been able to get a loop to trigger by hitting the canvas which is live processed via the mouse position (X: pan, Y: rate) cobbled together from examples.

I am running into an issue with making the images buttons that each call different samples. Pointers for that would be sick.

I also had the thought that I could get something to spit out the coordinates of each image – that is, on a mouse release the x, y position could be fed into the pan and rate parameters; or the pan and rate is dynamically related to the position of each image. Maybe each loop’s pan and rate read the mouse positions but only when clicking the respective images.

Any suggestions are welcome, particularly p5 references or examples of concepts that I may be unaware of. I have a working understanding of the language at best, but am still very green.

Hello

and welcome to the forum!

Great to have you here!

see this https://www.processing.org/examples/button.html

To get some serious help, please show your code.

my example which uses a class for the 2 images and stores the images in an array

see tutorials objects and arrays please: https://www.processing.org/tutorials/

Warm regards,

Chrisir

DraggingPic[] dragImages = new DraggingPic[2];

void setup() {
  size(1200, 600);
  background(255);   
  noStroke(); 

  dragImages[0] = new  DraggingPic(50, 50, "g2.JPG", 0); // change file names here! 
  dragImages[1] = new  DraggingPic(150, 50, "g2.JPG", 1);
}

void draw() { 
  background(255);   
  for (DraggingPic currentDraggingPic : dragImages) {
    currentDraggingPic.display();
    currentDraggingPic.mouseDragged();
  }//for
}

// ------------------------------------------

void mousePressed() {
  for (DraggingPic currentDraggingPic : dragImages) {
    currentDraggingPic.draggingpicMousePressed();
  }//for
}

void mouseReleased() {
  for (DraggingPic currentDraggingPic : dragImages) {
    currentDraggingPic.draggingpicMouseReleased();
  }//for
}

// ==================================================

class DraggingPic {

  int x;
  int y;
  PImage sample;
  int id; 

  // controls whether we are dragging (holding the mouse)
  boolean hold; 

  // constructor
  DraggingPic(int posx, int posy, 
    String imageNameAsString, 
    int id_) { 
    x=posx;
    y=posy;
    sample = loadImage(imageNameAsString);
    sample.resize(80, 0); // optional

    id=id_;
  }// constructor

  void display() {
    image(sample, x, y);
  }

  void draggingpicMousePressed() {
    if (mouseX>x&&
      mouseY>y&&
      mouseX<x+sample.width && 
      mouseY<y+sample.height) {
      hold=true;
    }
  }

  void draggingpicMouseReleased() {
    hold=false;
  }

  void mouseDragged() {
    if (hold) {
      x=mouseX; 
      y=mouseY;
      fill(0); 
      text("Soundfile: "
        +id
        + "; mouse: "
        +mouseX
        +", "
        +mouseY, 13, 18);
    }
  }//method
  //
}//class 
//

Ahh, okay, I think the “constructor” stuff I don’t understand. It also seems like the normal button stuff is easier for shapes? I looked at a bunch of tutorials and I don’t understand how to translate shape interactions to image interactions, or if there’s much of a different.

But yea, here’s my code you can see so far, it’s a mess because I grabbed some bits and bobs from elsewhere on the internet (and i know that “let” is common for p5 instead of “var”, but it didn’t seem important to change at the moment)—
oh and what this does, is
• load the two images randomly (because I didn’t yet figure out how to position them exactly with this code, and that’s actually ok), there’s an HTML file that has a video and image layer which the canvas sits onto of.
• when you click anywhere it triggers the audio file to play. I would like it to only play when clicking inside of the images
• the panning gets changed, but only on the initial click: like if I drag the image it doesn’t update, just stays int he spot where I first clicked, I then have to click again and it will switch. It would prefer it to update as I am dragging, then get locked into place when I release.

/*
  Johan Karlsson (DonKarlssonSan)
  Dragging images
*/
var Rectangle = (function () {
    function Rectangle(pos, img) {
        this.pos = pos;
        this.img = img;
        this.width = img.width;
        this.height = img.height;
    }
    Rectangle.prototype.draw = function () {
        image(this.img, this.pos.x, this.pos.y);
    };
    Rectangle.prototype.hits = function (hitpos) {
        if (hitpos.x > this.pos.x &&
            hitpos.x < this.pos.x + this.width &&
            hitpos.y > this.pos.y &&
            hitpos.y < this.pos.y + this.height) {
            return true;
        }
        return false;
    };
    return Rectangle;
}());
var rects;
var dragRec;
var isDragging;
var clickOffset;
var bee;
var flower;
var beeSound;
var reverb;


function preload() {
    bee = loadImage("bee.png");
    flower = loadImage("flower.png");
    beeSound = loadSound("bee-energy-2.mp3");
    beeSound.disconnect();
}
function setup() {
    rects = [];
    placeImages();
    isDragging = false;
    canvas = createCanvas(windowWidth, windowHeight);
    canvas.position(0, 0);
    reverb = new p5.Reverb();
    reverb.process(beeSound, 2, 0.2);
    reverb.amp(3);

}
function placeImages() {
   
        var pos = randomPos(bee.width, bee.height);
        rects.push(new Rectangle(pos, bee));

        var pos2 = randomPos(flower.width, flower.height);
        rects.push(new Rectangle(pos2, flower));

}
function randomPos(marginRight, marginBottom) {
    return createVector(random(0, windowWidth - marginRight), random(0, windowHeight - marginBottom));
}
function draw() {
    clear();
    rects.forEach(function (r) { return r.draw(); });
}
function mousePressed() {
    var m = createVector(mouseX, mouseY);
    var index;
    var panning = map(mouseX, 0, width, -1.0, 1.0);
    beeSound.play();
    beeSound.rate(1);
    rects.forEach(function (r, i) {
        if (r.hits(m)) {
            clickOffset = p5.Vector.sub(r.pos, m);
            isDragging = true;
            dragRec = r;
            index = i;
        }
    });
    if (isDragging) {
        putOnTop(index);
        beeSound.pan(panning);
    }
}
function putOnTop(index) {
    rects.splice(index, 1);
    rects.push(dragRec);
}
function mouseDragged() {
    if (isDragging) {
        var m = createVector(mouseX, mouseY);
        dragRec.pos.set(m).add(clickOffset);
        
    }
}
function mouseReleased() {
    isDragging = false;
    
}
function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
}

Thanks for sharing your code.

I don’t know about p5.js, so I can’t help you with that

This would be the bit https://www.processing.org/examples/button.html

or something like

 boolean mouseOver() {
    // returns true or false 
    return
      mouseX>x&&
      mouseY>y&&
      mouseX<x+img.width && 
      mouseY<y+img.height;
  }

(translate to p5.js where necessary)

see my example above

(translate to p5.js where necessary)

Warmest regards,

Chrisir

new version of above p5 file



// const for showing that an id is not valid
final int UNKNOWN=-1; 

// array of draggable images 
DraggingPic[] dragImages = new DraggingPic[2];

// the id of the image we are currently dragging 
int dragID=UNKNOWN; // at the moment, we don't drag anything, so we set the id to UNKNOWN

void setup() {
  size(1200, 600);
  background(255);   
  noStroke(); 

  // init 
  dragImages[0] = new  DraggingPic(50, 50, "g2.JPG", 0); // change file names here! 
  dragImages[1] = new  DraggingPic(150, 50, "g2.JPG", 1);
}

void draw() { 
  background(255);   
  int i=0; 
  for (DraggingPic currentDraggingPic : dragImages) {
    currentDraggingPic.displayData( i * 74 );
    if (currentDraggingPic.id!=dragID) {
      currentDraggingPic.display(dragID);
    }
    i++;
  }//for

  // when we have a drag image, show it again on top
  if (dragID!=UNKNOWN) {
    dragImages[dragID].mouseDragged();
    dragImages[dragID].display(dragID);
  }
}

// ------------------------------------------

void mousePressed() {
  // reset 
  dragID=UNKNOWN;
  // search a hit
  for (DraggingPic currentDraggingPic : dragImages) {
    int temp=currentDraggingPic.draggingpicMousePressed();
    if (temp!=UNKNOWN) {
      dragID=temp; 
      return;
    }
  }//for
}//func

void mouseReleased() {
  // reset
  // reset dragID
  dragID=UNKNOWN; // at the moment, we don't drag anything, so we set the id to UNKNOWN
  // reset hold in all images 
  for (DraggingPic currentDraggingPic : dragImages) {
    currentDraggingPic.draggingpicMouseReleased();
  }//for
}//func

// ==================================================

class DraggingPic {

  float x; // pos
  float y;
  PImage sample; // image 
  int id; // identifier 

  // indicates whether we are dragging (holding the mouse) or not
  boolean hold=false; 

  // basically we herewith neutralize the point where you click inside the image when you start dragging (near upper left corner or near lower right corner) - we don't want the image to jump to the mouse pos at start of dragging  
  float offsetX, offsetY; 

  // constructor
  DraggingPic(int posx, int posy, 
    String imageNameAsString, 
    int id_) { 
    x=posx;
    y=posy;
    sample = loadImage(imageNameAsString);
    sample.resize(80, 0); // optional

    id=id_;
  }// constructor

  void display(int dragID_) {
    // show image 
    // if mouse over or hold
    if (mouseOver()||hold) {
      // when we drag one image over another, we don't one the latter to have a frame falsely
      if (!(dragID_!=id && dragID_!=UNKNOWN)) {
        // Yes, mouse over
        // red frame, small, outside 
        int frameThickness = 11; 
        fill(255, 0, 0); 
        stroke(255, 0, 0);
        noFill(); 
        rect(x-frameThickness, y-frameThickness, 
          sample.width+2*frameThickness, sample.height+2*frameThickness );

        // red frame, thick, inside, near image
        frameThickness = 3; 
        fill(255, 0, 0); 
        noStroke();
        rect(x-frameThickness, y-frameThickness, 
          sample.width+2*frameThickness, sample.height+2*frameThickness );
      }//if
    }//if
    // show image on top the two frames 
    image(sample, x, y);
  }//method

  int draggingpicMousePressed() {
    // start dragging? 
    if (mouseOver()) {
      // yes, we are inside  
      // record the distance between image pos and mouse pos at the start of dragging
      offsetX=mouseX-x; 
      offsetY=mouseY-y;  
      // set as being dragged 
      hold=true;
      return id;
    }
    return UNKNOWN;
  }

  void draggingpicMouseReleased() {
    // stop dragging 
    hold=false;
  }

  void mouseDragged() {
    // during dragging: change image pos
    if (hold) {
      // give new pos to image and use offset 
      x=mouseX-offsetX; 
      y=mouseY-offsetY;
      // text 
      fill(0); 
      text("Soundfile: "
        +id
        + "; image pos: "
        +x
        +", "
        +y, 13, 18);
    }//if
  }//method

  void displayData(int y_) {
    //
    // text 
    fill(0); 
    text("Soundfile: "
      +id
      + "; image pos: "
      +x
      +", "
      +y, width-64, y_, 
      60, height);
  }//

  boolean mouseOver() {
    // returns true or false 
    return
      mouseX>x&&
      mouseY>y&&
      mouseX<x+sample.width && 
      mouseY<y+sample.height;
  }
  //
}//class 
//

Thank you for the help! I looked through your code, then looked back at mine, then looked through a bunch of the references, then looked at tutorials for several hours, just to make sure I knew what I was doing. Most of the code I was using was found and I realized a lot of it was fat that could be shaved off.

I tore down what I had and I’ve been able to do exactly what I wanted by recoding it again. This time much leaner, though it is a little more taxing on my machine it seems.

For the sake of closure, here is what I have:


let bee;
let bx;
let by;
let beeSound;
let bv = 0.8;
let flower;
let fx;
let fy;
let flowerSound;
let fv = 0.8;
let reverb;

function preload() {

    beeSound = loadSound("bee-energy-2.mp3");
    beeSound.disconnect();
    flowerSound = loadSound("bee-energy-1.mp3");
    flowerSound.disconnect();
    
}

function setup() {
    
 
    canvas = createCanvas(windowWidth, windowHeight);
    canvas.position(0, 0);
    bee = loadImage("bee.png");
    bx = windowWidth/2;
    by = windowHeight/2;
    flower = loadImage("flower.png");
    fx = windowWidth/2.5;
    fy = windowHeight/3;
    reverb = new p5.Reverb();
    reverb.process(beeSound, 2, 0.2);
    reverb.process(flowerSound, 2, 0.2);
        
    
    
}
function draw() {
    clear();
    imageMode(CENTER);
    image(bee, bx, by);
    image(flower, fx, fy);
    
    

}

function mouseDragged() {
  
    let panning = map(mouseX, 0, width, -1.0, 1.0);
    let speed = map(mouseY, 0.1, height, 0, 2);
    speed = constrain(speed, 0.01, 2);
  
    if(dist(bx, by, mouseX, mouseY) < bee.width/2) {

          bx = mouseX;
          by = mouseY;
          beeSound.play();
          beeSound.pan(panning);
          beeSound.rate(speed);
          
    
    } else if(dist(fx, fy, mouseX, mouseY) < flower.width/2) {
         
          fx = mouseX;
          fy = mouseY;
          flowerSound.play();
          flowerSound.pan(panning);
          flowerSound.rate(speed);
    }
}

function mouseWheel(event) {
 let volume = map(event.delta, -100, 100, 0, 1);
 
 if(dist(bx, by, mouseX, mouseY) < bee.width/2) {

          beeSound.setVolume(volume);
          
    
 } else if(dist(fx, fy, mouseX, mouseY) < flower.width/2) {
         
         flowerSound.setVolume(volume);
 }

}

The mouseWheel as a volume control needs some work, I just slapped it in a few minutes ago.

1 Like