fbpx
<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>