All posts

interaction

1 post

p5.js Session 16: Mouse Interaction

In Session 14 the sketch listened to you once. You pressed a key, it saved a file, and that was the end of the conversation. This session leaves the line open: the sketch watches the mouse on every frame and answers immediately.

mouseX and mouseY

mouseX and mouseY hold the pointer’s position on the canvas, and p5.js refreshes them before every pass of draw(). Read them, and whatever you draw follows the mouse.

A ring of bars radiating from the centre. Move left and right to change how many there are, up and down to change how long they are.

The five colours are Vibrant Color Fiesta, another set from the trending palettes on Coolors.

let palette;

function setup() {
  createCanvas(600, 600);
  frameRate(30);
  angleMode(DEGREES);
  noStroke();
  palette = ['#FFBE0B', '#FB5607', '#FF006E', '#8338EC', '#3A86FF'];
}

function draw() {
  background(0);
  push();
  translate(300, 300);
  let count = floor(mouseX / 20) + 3;
  let barLength = mouseY / 3 + 80;
  let angle = 360 / count;
  for (let i = 0; i < count; i++) {
    rotate(angle);
    fill(palette[i % palette.length]);
    rect(0, -10, barLength, 20);
  }
  pop();
  fill(255);
  circle(mouseX, mouseY, 12);
}

function keyPressed() {
  if (key === 'g' || key === 'G') {
    saveGif('mouse-bars', 20);
  }
}

  • mouseX and mouseY are just numbers, updated for you before every frame. Nothing else about them is special.
  • count turns mouseX into a number of bars, three at the far left rising to thirty-three at the far right.
  • barLength turns mouseY into a length, 80 pixels at the top of the canvas and 280 at the bottom.
  • palette[i % palette.length] cycles the five colours, the trick from Session 13, so bar six starts the palette again.
  • The white dot is drawn at mouseX, mouseY after pop(), so the translate() does not drag it away from the pointer.

The dot is there because the pointer itself is not part of the canvas. Drawing a circle at mouseX, mouseY is the simplest possible proof that the two numbers really are where your hand is.

mousePressed()

Reading the mouse is one thing. Reacting to a click is another, and it needs a function p5.js calls for you, the same arrangement as keyPressed().

Every click drops a ring at that point, and every ring grows until it leaves the canvas.

let palette;
let clickX = [];
let clickY = [];
let ringSize = [];
let ringColour = [];
let clicks = 0;

function setup() {
  createCanvas(600, 600);
  frameRate(30);
  noFill();
  strokeWeight(6);
  palette = ['#FFBE0B', '#FB5607', '#FF006E', '#8338EC', '#3A86FF'];
}

function draw() {
  background(0);
  for (let i = 0; i < clicks; i++) {
    ringSize[i] = ringSize[i] + 4;
    stroke(ringColour[i]);
    circle(clickX[i], clickY[i], ringSize[i]);
  }
  push();
  noStroke();
  fill(255);
  circle(mouseX, mouseY, 12);
  pop();
}

function mousePressed() {
  clickX[clicks] = mouseX;
  clickY[clicks] = mouseY;
  ringSize[clicks] = 10;
  ringColour[clicks] = palette[floor(random(palette.length))];
  clicks = clicks + 1;
}

function keyPressed() {
  if (key === 'g' || key === 'G') {
    saveGif('click-rings', 20);
  }
}

  • mousePressed() is called for you, once, each time the button goes down. You declare it and never call it, exactly like keyPressed().
  • Each click records four things: where it happened, a starting size of 10, a colour picked at random from the palette, and one more on the clicks count.
  • draw() adds four pixels to every ring on every frame, so the old ones keep growing while the new ones appear behind them.
  • noFill() and stroke() are what make these outlines rather than solid discs.
  • The white dot needs a fill and no stroke, the opposite of the rings, so push() and pop() wrap it and put the settings back.

One naming trap worth knowing about. JavaScript arrays have a push() method of their own, which would be the usual way to add to lists like these. This sketch uses a counter and direct indexing instead, because push() already means something else entirely in p5.js, saving the drawing state, and two unrelated things sharing a name is a trap rather than a lesson.

The scripts in this session were written for p5.js 2.3.2, the version the web editor runs by default.


Next session is the last of this series, and it looks back over all seventeen.