📄 interaction.md

← Vault

Interaction

Mouse Events

Continuous State

`javascript

mouseX, mouseY // current position (relative to canvas)

pmouseX, pmouseY // previous frame position

mouseIsPressed // boolean

mouseButton // LEFT, RIGHT, CENTER (during press)

movedX, movedY // delta since last frame

winMouseX, winMouseY // relative to window (not canvas)

`

Event Callbacks

`javascript

function mousePressed() {

// fires once on press

// mouseButton tells you which button

}

function mouseReleased() {

// fires once on release

}

function mouseClicked() {

// fires after press+release (same element)

}

function doubleClicked() {

// fires on double-click

}

function mouseMoved() {

// fires when mouse moves (no button pressed)

}

function mouseDragged() {

// fires when mouse moves WITH button pressed

}

function mouseWheel(event) {

// event.delta: positive = scroll down, negative = scroll up

zoom += event.delta * -0.01;

return false; // prevent page scroll

}

`

Mouse Interaction Patterns

Spawn on click:

`javascript

function mousePressed() {

particles.push(new Particle(mouseX, mouseY));

}

`

Mouse follow with spring:

`javascript

let springX, springY;

function setup() {

springX = new Spring(width/2, width/2);

springY = new Spring(height/2, height/2);

}

function draw() {

springX.setTarget(mouseX);

springY.setTarget(mouseY);

let x = springX.update();

let y = springY.update();

ellipse(x, y, 50);

}

`

Drag interaction:

`javascript

let dragging = false;

let dragObj = null;

let offsetX, offsetY;

function mousePressed() {

for (let obj of objects) {

if (dist(mouseX, mouseY, obj.x, obj.y) < obj.radius) {

dragging = true;

dragObj = obj;

offsetX = mouseX - obj.x;

offsetY = mouseY - obj.y;

break;

}

}

}

function mouseDragged() {

if (dragging && dragObj) {

dragObj.x = mouseX - offsetX;

dragObj.y = mouseY - offsetY;

}

}

function mouseReleased() {

dragging = false;

dragObj = null;

}

`

Mouse repulsion (particles flee cursor):

`javascript

function draw() {

let mousePos = createVector(mouseX, mouseY);

for (let p of particles) {

let d = p.pos.dist(mousePos);

if (d < 150) {

let repel = p5.Vector.sub(p.pos, mousePos);

repel.normalize();

repel.mult(map(d, 0, 150, 5, 0));

p.applyForce(repel);

}

}

}

`

Keyboard Events

State

`javascript

keyIsPressed // boolean

key // last key as string ('a', 'A', ' ')

keyCode // numeric code (LEFT_ARROW, UP_ARROW, etc.)

`

Event Callbacks

`javascript

function keyPressed() {

// fires once on press

if (keyCode === LEFT_ARROW) { / ... / }

if (key === 's') saveCanvas('output', 'png');

if (key === ' ') CONFIG.paused = !CONFIG.paused;

return false; // prevent default browser behavior

}

function keyReleased() {

// fires once on release

}

function keyTyped() {

// fires for printable characters only (not arrows, shift, etc.)

}

`

Continuous Key State (Multiple Keys)

`javascript

let keys = {};

function keyPressed() { keys[keyCode] = true; }

function keyReleased() { keys[keyCode] = false; }

function draw() {

if (keys[LEFT_ARROW]) player.x -= 5;

if (keys[RIGHT_ARROW]) player.x += 5;

if (keys[UP_ARROW]) player.y -= 5;

if (keys[DOWN_ARROW]) player.y += 5;

}

`

Key Constants

`

LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW

BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE

SHIFT, CONTROL, OPTION, ALT

`

Touch Events

`javascript

touches // array of { x, y, id } — all current touches

function touchStarted() {

// fires on first touch

return false; // prevent default (stops scroll on mobile)

}

function touchMoved() {

// fires on touch drag

return false;

}

function touchEnded() {

// fires on touch release

}

`

Pinch Zoom

`javascript

let prevDist = 0;

let zoomLevel = 1;

function touchMoved() {

if (touches.length === 2) {

let d = dist(touches[0].x, touches[0].y, touches[1].x, touches[1].y);

if (prevDist > 0) {

zoomLevel *= d / prevDist;

}

prevDist = d;

}

return false;

}

function touchEnded() {

prevDist = 0;

}

`

DOM Elements

Creating Controls

`javascript

function setup() {

createCanvas(800, 800);

// Slider

let slider = createSlider(0, 255, 100, 1); // min, max, default, step

slider.position(10, height + 10);

slider.input(() => { CONFIG.value = slider.value(); });

// Button

let btn = createButton('Reset');

btn.position(10, height + 40);

btn.mousePressed(() => { resetSketch(); });

// Checkbox

let check = createCheckbox('Show grid', false);

check.position(10, height + 70);

check.changed(() => { CONFIG.showGrid = check.checked(); });

// Select / dropdown

let sel = createSelect();

sel.position(10, height + 100);

sel.option('Mode A');

sel.option('Mode B');

sel.changed(() => { CONFIG.mode = sel.value(); });

// Color picker

let picker = createColorPicker('#ff0000');

picker.position(10, height + 130);

picker.input(() => { CONFIG.color = picker.value(); });

// Text input

let inp = createInput('Hello');

inp.position(10, height + 160);

inp.input(() => { CONFIG.text = inp.value(); });

}

`

Styling DOM Elements

`javascript

let slider = createSlider(0, 100, 50);

slider.position(10, 10);

slider.style('width', '200px');

slider.class('my-slider');

slider.parent('controls-div'); // attach to specific DOM element

`

Audio Input (p5.sound)

Requires p5.sound.min.js addon.

`html

`

Microphone Input

`javascript

let mic, fft, amplitude;

function setup() {

createCanvas(800, 800);

userStartAudio(); // required — user gesture to enable audio

mic = new p5.AudioIn();

mic.start();

fft = new p5.FFT(0.8, 256); // smoothing, bins

fft.setInput(mic);

amplitude = new p5.Amplitude();

amplitude.setInput(mic);

}

function draw() {

let level = amplitude.getLevel(); // 0.0 to 1.0 (overall volume)

let spectrum = fft.analyze(); // array of 256 frequency values (0-255)

let waveform = fft.waveform(); // array of 256 time-domain samples (-1 to 1)

// Get energy in frequency bands

let bass = fft.getEnergy('bass'); // 20-140 Hz

let lowMid = fft.getEnergy('lowMid'); // 140-400 Hz

let mid = fft.getEnergy('mid'); // 400-2600 Hz

let highMid = fft.getEnergy('highMid'); // 2600-5200 Hz

let treble = fft.getEnergy('treble'); // 5200-14000 Hz

// Each returns 0-255

}

`

Audio File Playback

`javascript

let song, fft;

function preload() {

song = loadSound('track.mp3');

}

function setup() {

createCanvas(800, 800);

fft = new p5.FFT(0.8, 512);

fft.setInput(song);

}

function mousePressed() {

if (song.isPlaying()) {

song.pause();

} else {

song.play();

}

}

`

Beat Detection (Simple)

`javascript

let prevBass = 0;

let beatThreshold = 30;

let beatCooldown = 0;

function detectBeat() {

let bass = fft.getEnergy('bass');

let isBeat = bass - prevBass > beatThreshold && beatCooldown <= 0;

prevBass = bass;

if (isBeat) beatCooldown = 10; // frames

beatCooldown--;

return isBeat;

}

`

Scroll-Driven Animation

`javascript

let scrollProgress = 0;

function setup() {

let canvas = createCanvas(windowWidth, windowHeight);

canvas.style('position', 'fixed');

// Make page scrollable

document.body.style.height = '500vh';

}

window.addEventListener('scroll', () => {

let maxScroll = document.body.scrollHeight - window.innerHeight;

scrollProgress = window.scrollY / maxScroll;

});

function draw() {

background(0);

// Use scrollProgress (0 to 1) to drive animation

let x = lerp(0, width, scrollProgress);

ellipse(x, height/2, 50);

}

`

Responsive Events

`javascript

function windowResized() {

resizeCanvas(windowWidth, windowHeight);

// Recreate buffers

bgLayer = createGraphics(width, height);

// Recalculate layout

recalculateLayout();

}

// Visibility change (tab switching)

document.addEventListener('visibilitychange', () => {

if (document.hidden) {

noLoop(); // pause when tab not visible

} else {

loop();

}

});

`