Chanomic Sketch

Calm Bubbles

let bubbles = [];
const MAX_BUBBLES = 120;

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(10, 20, 30);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  background(10, 20, 30);
}

function draw() {
  background(10, 20, 30, 55);

  if (bubbles.length < MAX_BUBBLES && random() < 0.4) {
    bubbles.push(new Bubble());
  }

  for (let i = bubbles.length - 1; i >= 0; i--) {
    bubbles[i].update();
    bubbles[i].draw();

    if (bubbles[i].isDead()) {
      bubbles.splice(i, 1);
    }
  }
}

class Bubble {
  constructor() {
    this.pos = createVector(
      random(width),
      height + random(50, 150)
    );

    this.vel = createVector(
      random(-0.15, 0.15),
      random(-0.8, -1.4)
    );

    this.acc = createVector(0, 0);

    this.r = random(12, 40);

    this.buoyancy = random(0.016, 0.024);
    this.drag = random(0.98, 0.992);

    this.noiseSeed = random(1000);
  }

  applyForce(f) {
    this.acc.add(f);
  }

  update() {
    // 浮力(強め)
    this.applyForce(createVector(0, -this.buoyancy));

    // 水流ノイズ
    const n =
      noise(this.noiseSeed, frameCount * 0.01) - 0.5;
    this.applyForce(createVector(n * 0.025, 0));

    // 物理更新
    this.vel.add(this.acc);
    this.vel.mult(this.drag);
    this.pos.add(this.vel);

    this.acc.mult(0);
  }

  draw() {
    noFill();
    stroke(200, 220, 255);
    strokeWeight(1.2);
    ellipse(this.pos.x, this.pos.y, this.r * 2);
  }

  isDead() {
    return this.pos.y < -this.r;
  }
}