<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js">
<script>
let birdSteps = [];
let birds = [];
let waveY;
let waveSpeed;
let waveNoiseSeed;
let waveHeight = 300;
let mouseEffectRadius = 100; // Radius of mouse effect causing birds to fly over waves
function setup() {
createCanvas(windowWidth, windowHeight);
waveY = height / 2;
waveSpeed = 2;
waveNoiseSeed = random(100);
// Initialize birds at random positions
for (let i = 0; i < 20; i++) {
birds.push(new Bird(random(width), random(height)));
}
}
function draw() {
background(220);
drawWaves();
birds.forEach(bird => {
bird.flock(birds);
bird.avoidWaves();
bird.reactToMouse(mouseX, mouseY, mouseEffectRadius);
bird.update();
bird.edges();
bird.show();
});
updateAndDrawBirdSteps();
waveY += waveSpeed;
if (frameCount % 60 === 0) {
waveSpeed = random(-2, 2); // Randomize wave speed for dynamic movement
waveNoiseSeed += random(-0.1, 0.1); // Adjust noise seed for wave variation
}
}
class Bird {
constructor(x, y) {
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D();
this.velocity.setMag(random(1, 2)); // Slower initial speed
this.acceleration = createVector();
this.maxForce = 0.1; // Lower force for more gradual turns
this.maxSpeed = 2; // Lower max speed for slower movement overall
this.perceptionRadius = 50;
}
edges() {
if (this.position.x > width) this.position.x = 0;
else if (this.position.x < 0) this.position.x = width;
if (this.position.y > height) this.position.y = 0;
else if (this.position.y < 0) this.position.y = height;
}
align(birds) {
let steering = createVector();
let total = 0;
for (let other of birds) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other != this && d < this.perceptionRadius) {
steering.add(other.velocity);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
cohesion(birds) {
let steering = createVector();
let total = 0;
for (let other of birds) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other != this && d < this.perceptionRadius) {
steering.add(other.position);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.sub(this.position);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
separation(birds) {
let steering = createVector();
let total = 0;
for (let other of birds) {
let d = dist(this.position.x, this.position.y, other.position.x, other.position.y);
if (other != this && d < this.perceptionRadius) {
let diff = p5.Vector.sub(this.position, other.position);
diff.div(d);
steering.add(diff);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
flock(birds) {
let alignment = this.align(birds);
let cohesion = this.cohesion(birds);
let separation = this.separation(birds);
this.acceleration.add(alignment);
this.acceleration.add(cohesion);
this.acceleration.add(separation);
}
avoidWaves() {
let waveTopY = waveY + noise(waveNoiseSeed, this.position.x * 0.005) * waveHeight;
if (this.position.y < waveTopY) {
this.acceleration.y += 0.05; // Move downwards, away from the wave
} else {
this.acceleration.y -= 0.05; // Move upwards, towards the wave
}
}
reactToMouse(mouseX, mouseY, radius) {
let d = dist(mouseX, mouseY, this.position.x, this.position.y);
if (d < radius) {
let flee = createVector(this.position.x - mouseX, this.position.y - mouseY);
flee.setMag(this.maxSpeed);
this.acceleration.add(flee);
}
}
update() {
this.position.add(this.velocity);
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxSpeed);
this.acceleration.mult(0);
}
show() {
strokeWeight(8);
stroke(255, 100);
point(this.position.x, this.position.y);
}
}
function drawWaves() {
waveNoiseSeed += 0.01;
fill('#62e3ff'); // Solid color for water
stroke(0, 0, 255, 50); // Keeping the line color
beginShape();
vertex(0, height); // Start at bottom left of screen
for (let x = 0; x <= width; x += 10) {
let y = noise(waveNoiseSeed, x * 0.005) * waveHeight;
vertex(x, y + waveY); // Adjust y position based on waveY and waveHeight
}
vertex(width, height); // End at bottom right of screen
endShape(CLOSE);
}
function updateAndDrawBirdSteps() {
let currentWaveTopY = waveY + noise(waveNoiseSeed) * waveHeight;
birdSteps = birdSteps.filter(step => step.y > currentWaveTopY); // Keep steps only in "sand"
birdSteps.forEach(step => {
fill(0);
noStroke();
ellipse(step.x, step.y, 3, 3); // Smaller steps
});
// Birds leave steps in sand, avoiding waves
birds.forEach(bird => {
if (random(1) < 0.1 && bird.position.y > currentWaveTopY) {
birdSteps.push(bird.position.copy());
}
});
}</script>