xxxxxxxxxx
///////////////////////
// Rocking_Bird
///////////////////////
const bgColors = ['#65C4C4', '#A075D8 ', '#DDA57A '];
var bgIndex = 0;
var mySound;
var amplitude;
var initT = 150;
var t = initT;
var rocking = false;
// body
var sideNum = 6;
// :: Beat Detect Variables
// how many draw loop frames before the beatCutoff starts to decay
// so that another beat can be triggered.
// frameRate() is usually around 60 frames per second,
// so 20 fps = 3 beats per second, meaning if the song is over 180 BPM,
// we wont respond to every beat.
var beatHoldFrames = 60;
// what amplitude level can trigger a beat?
var beatThreshold = 0.02;
// When we have a beat, beatCutoff will be reset to 1.1*beatThreshold, and then decay
// Level must be greater than beatThreshold and beatCutoff before the next beat can trigger.
var beatCutoff = 0;
var beatDecayRate = 0.98; // how fast does beat cutoff decay?
var framesSinceLastBeat = 0; // once this equals beatHoldFrames, beatCutoff starts to decay.
function preload() {
soundFormats('mp3', 'ogg');
mySound = loadSound('9');
}
function setup() {
createCanvas(600, 600);
amplitude = new p5.Amplitude();
// mySound.play();
amplitude.setInput(mySound);
amplitude.smooth(0.9);
}
function draw() {
const t1 = millis();
var level = amplitude.getLevel();
detectBeat(level);
if (rocking) {
t += 15;
if (t > 500 * PI + initT) {
t = initT;
rocking = false;
}
}
// background(bgColors[floor(millis() / 500) % bgColors.length]);
background(bgColors[bgIndex]);
push();
translate(width / 2, height / 2);
translate(-30, 70);
const bodyPos = createVector(
30 * sin(t * 0.004) - 10 * cos(t * 0.004),
20 * sin(t * 0.004) + 5 * cos(t * 0.004),
).add(
0,
10 * sin(t * 0.02 + 0.3),
).add(
20,
-100,
);
const bodyPos1 = createVector(
30 * sin(t1 * 0.004) - 10 * cos(t1 * 0.004),
20 * sin(t1 * 0.004) + 5 * cos(t1 * 0.004),
).add(
0,
10 * sin(t1 * 0.02 + 0.3),
).add(
20,
-100,
);
const pelvisPos = createVector(
-10 + 40 * sigmoid(sin(t * 0.004 + 0.6)),
0,
).lerp(bodyPos1.copy().add(0, 50), 0.7);
// const headPos = createVector(
// 70 + 40 * sigmoid(sin(t * 0.004 + 0.7)),
// -150 + 5 * sin(t * 0.012),
// );
// const headPos = bodyPos.copy().add(20, -8);
const headPos = bodyPos1.copy().add(20, -8);
const leftFootPos = createVector(
-20 + 50 * sigmoid(5 * sin(t * 0.004)),
-30 * max(1 - abs(sin(t * 0.004)) - 0.5, 0) + 50,
);
const rightFootPos = createVector(
40 + 50 * sigmoid(5 * sin(t * 0.004 + 1.2)),
-30 * max(1 - abs(sin(t * 0.004 + 1.2)) - 0.5, 0) + 50,
);
const leftAnklePos = createVector(
-50 + 50 * sigmoid(5 * sin(t * 0.004)),
-30 * max(1 - abs(sin(t * 0.004 + 0.3)) - 0.1, 0) + 50,
);
const rightAnklePos = createVector(
10 + 50 * sigmoid(5 * sin(t * 0.004 + 1.2)),
-30 * max(1 - abs(sin(t * 0.004 + 1.5)) - 0.1, 0) + 50,
);
leg(pelvisPos, rightAnklePos, rightFootPos);
push();
noStroke();
// mouth
// fill('#575755');
// teardrop(headPos.copy().add(40, 10), headPos.copy().add(150, 70), 10, 2);
push();
// drawingContext.shadowColor = '#918f7e';
// drawingContext.shadowBlur = pixelDensity() * 10;
fill('#d8d8d8');
// head
// teardrop(headPos, bodyPos, 50, 80);
// body
push();
translate(bodyPos1.x, bodyPos1.y);
drawBody1(110, sideNum)
// body(t);
pop();
// mouth
fill('#f7b500');
teardrop(headPos.copy().add(50, 10), headPos.copy().add(120, 30), 12, 3);
pop();
// eye
fill(0);
circle(
headPos.x + 10,
headPos.y - 10,
25,
);
pop();
leg(pelvisPos.add(-10, 5), leftAnklePos, leftFootPos);
pop();
}
function detectBeat(level) {
if (level > beatCutoff && level > beatThreshold) {
onBeat();
beatCutoff = level * 1.2;
framesSinceLastBeat = 0;
} else {
if (framesSinceLastBeat <= beatHoldFrames) {
framesSinceLastBeat++;
}
else {
beatCutoff *= beatDecayRate;
beatCutoff = Math.max(beatCutoff, beatThreshold);
}
}
}
function onBeat() {
if (!rocking) {
rocking = true;
if (sideNum < 50) {
sideNum += 2;
} else {
sideNum = 6;
}
if (bgIndex < bgColors.length - 1) {
bgIndex++;
} else {
bgIndex = 0;
}
}
}
function keyPressed() {
if (key === ' ') {
// playing a sound file on a user gesture
// is equivalent to `userStartAudio()`
mySound.play();
}
}
function mousePressed() {
if (mouseButton === LEFT) {
saveCanvas('myCanvas', 'png');
}
}
function body(t) {
rotate(0.12 * sin(t * 0.004 + 0.7));
teardrop(
createVector(0, 0),
createVector(-150, 50),
80,
30,
);
}
// function drawBody(r, nm) {
// push();
// beginShape();
// for (let i = PI; i < TWO_PI + PI; i += TWO_PI / 360) {
// let radius = r - (((r / nm)) * sin(i * nm));
// let ex = radius * sin(i);
// let ey = radius * cos(i);
// vertex(ex, ey)
// }
// endShape(CLOSE)
// pop();
// }
function drawBody1(_r, nm) {
let draw_r = 0;
let R = _r;
let r = R * 0.8;
push();
rotate(PI / nm);
beginShape();
for (let i = 0; i < nm; i++) {
if (i % 2 == 0) {
draw_r = R;
} else {
draw_r = r;
}
vertex(draw_r * cos(TWO_PI * i / nm), draw_r * sin(TWO_PI * i / nm));
}
endShape(CLOSE);
pop();
}
function sigmoid(x) {
return 1 / (1 + Math.exp(-x));
}
// Draw a shape resembling a teardrop, which is just two circles
// of differing radii, connected into one shape.
function teardrop(p1, p2, r1, r2) {
const angle = p2.copy().sub(p1).heading() + PI;
const rDiff = r1 - r2;
const dist = p1.dist(p2);
const angleDist = atan2(rDiff, dist);
const path = new Path2D();
path.arc(
p1.x,
p1.y,
r1,
-(PI / 2 + angleDist) + angle,
PI / 2 + angleDist + angle,
);
path.arc(
p2.x,
p2.y,
r2,
-(PI / 2 - angleDist) + PI + angle,
PI / 2 - angleDist + PI + angle,
);
path.closePath();
if (_renderer._doFill) {
drawingContext.fill(path)
};
if (_renderer._doStroke) {
drawingContext.stroke(path)
};
}
function leg(torso, ankle, foot) {
const segmentLength = Math.max(50, torso.dist(ankle) / 2 + 5);
const mid = torso.copy().lerp(ankle, 0.5);
const tangent = ankle.copy().sub(torso).normalize();
const normal = createVector(-tangent.y, tangent.x);
const tangentDist = torso.dist(mid);
// Some basic IK to figure out the position of the joint of the leg.
// The leg has two segments, each `segmentLength` long. These form
// two triangles. The hypotenuse will be `segmentLength` (that's
// where the leg is, and what we'll draw), and one side, `tangentDist`,
// is halfway from the torso to the ankle. We'll use the Pythagorean
// Theorem to find the remaining side.
//
// --|
// ---/ |
// segmentLength --/ |
// ---/ | tangentDist
// ---/ |
// -/ |
// ---------------------
// normalDist
//
// segmentLength^2 = tangentDist^2 + normalDist^2
// normalDist = sqrt(segmentLength^2 - tangentDist^2)
const normalDist = sqrt(pow(segmentLength, 2) - pow(tangentDist, 2));
const joint = mid.copy().add(normal.copy().mult(normalDist));
push();
noFill();
strokeJoin(ROUND);
strokeWeight(6);
line(ankle.x - 12, ankle.y, ankle.x, ankle.y);
beginShape();
vertex(torso.array());
vertex(joint.array());
vertex(ankle.array());
vertex(foot.array());
vertex(foot.x - 2, foot.y + 8);
endShape();
pop();
}