Help with animation doppler sound-wave propagation

Hi!
I am trying to create an abstract animation visualizing how sound waves propagate emitting from a moving object. Basically circles growing in radius while the x position of the object changes.

Like in this animation:

In my code I am missing the characteristic of a sphere constantly emitted from the object. Something needs to be done with opacity and re-trigger. To match the characteristics of the animation in the youtube video. Maybe some one has an idea that could help me? Thank you!

Here is what I got:

let xpos = 0;
let ypos = 200;
let radius = 0;
let interval = 0.5;
let waves = [];
function setup() {
  createCanvas(800, 400);
}

function draw() {
  background(0, 255);
  xpos += 6;
// object emitting sound
    fill(255);
    ellipse(xpos + 6, ypos, 10);
  
  
  
  for (let i = 0; i < 1; i++) {
    waves.push(new Wave(xpos, ypos, radius));
  }

  for (let wave of waves) {
    wave.update();
    wave.show();
  }

  for (let i = waves.length - 1; i >= 0; i--) {
    if (waves[i].finished()) {
      waves.splice(i, 1);
    }
  }
}

class Wave {
  constructor(x, y, radius) {
    this.pos = createVector(x, y);
    this.radius = radius;
    this.lifetime = 255;
  }

  finished() {
    return this.lifetime < 0;
  }

  update() {
    this.radius += 40;
    this.lifetime -= 9;
  }

  show() {
    stroke(255, this.lifetime);
    strokeWeight(2);
    noFill(0);
    stroke(255, this.lifetime);
    strokeWeight(1);
    ellipse(this.pos.x, this.pos.y, this.radius);
  }
}

maybe it’s a kind of optical illusion

when a circle increases its radius, it takes the position of the last circle and so on. So it looks like nothing moves but the circle do move.

you can see this with random speed of circle growing:

let xpos = 9;
let ypos = 200;
let radius = 20;
let interval = 0.5;
let waves = [];

function setup() {
  createCanvas(800, 400);
  frameRate(1);
}

function draw() {
  background(0, 255);

  xpos += 15;
  // object emitting sound
  fill(255);
  ellipse(xpos + 6, ypos, 10);

  for (let i = 0; i < 2; i++) {
    waves.push(new Wave(xpos, ypos, radius));
  }

  for (let wave of waves) {
    wave.update();
    wave.show();
  }

  for (let i = waves.length - 1; i >= 0; i--) {
    if (waves[i].finished()) {
      waves.splice(i, 1);
    }
  }
}

class Wave {
  constructor(x, y, radius) {
    this.pos = createVector(x, y);
    this.radius = radius;
    this.lifetime = 255;
    this.speed = random(17, 88);
  }

  finished() {
    return this.lifetime < 0;
  }

  update() {
    this.radius += this.speed;
    this.lifetime -= 9;
  }

  show() {
    stroke(255, this.lifetime);
    strokeWeight(2);
    noFill(0);
    // stroke(255, this.lifetime);
    stroke(255);
    strokeWeight(1);
    ellipse(this.pos.x, this.pos.y, this.radius);
  }
}

2 Likes

@Chrisir that´s interesting… Thank you! Experimenting further here

1 Like

@Chrisir I think I made it look a bit better through some additional opacity animation
It still looks unexpected but is should be a quiet adequate representation as it looks like in this more scientific visualization I found here:

let xpos = 0;
let ypos = 200;
let radius = 80;
let interval = 5;
let waves = [];
let opacity = 0;

function setup() {
  createCanvas(windowWidth, windowHeight);
  frameRate(20);
}

function draw() {
  background(0);
  if (frameCount % (interval * 2) == 0) {
    opacity = 180;
  } else {
    opacity = 50;
  }

  // object emitting sound moving
  xpos = 800 * sin(((frameCount % 60) * TAU) / 60) + width / 2;
  ypos = 200;

  fill(255);
  noStroke(0);
  ellipse(xpos, ypos, 30);
  


  //generate soundvaves
  for (let i = 0; i < 1; i++) {
    waves.push(new Wave(xpos, ypos, radius, opacity));
  }

  for (let wave of waves) {
    wave.update();
    wave.show();
  }
  
//loudness out finish
  for (let i = waves.length - 1; i >= 0; i--) {
    if (waves[i].finished()) {
      waves.splice(i, 1);
    }
  }
}
//soundwaves
class Wave {
  constructor(xpos, ypos, radius, opacity) {
    this.pos = createVector(xpos, ypos);
    this.radius = radius;
    this.lifetime = 255;
    this.speed = 200;
    this.opacity = opacity;
  }

  finished() {
    return this.lifetime < 0;
  }

  update() {
    this.radius += this.speed;
    this.lifetime -= 30;
  }

  show() {
    noFill(0);
    stroke(this.opacity, this.lifetime);
    strokeWeight(4);
    ellipse(this.pos.x, this.pos.y, this.radius);
  }
}

1 Like

mhhh after watching this video I just posted from youtube… maybe I could jet a smoother result if I would scale the circles instead of increasing the radius…

or maybe it looks better because it all happens slower…

1 Like

I´ll try some more tomorrow again sigh :slight_smile:

1 Like

Finally I have it. I guess it was a bad idea to start from an old particle emiter patch… that handled the array differently… But now I started from scratch and have it working!

let timer = 0;
let soundwaves = [];
let period = 50;
let y = 0;
let x = 0;

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function draw() {
  background(0);
  y = windowHeight / 2;
  x = 900 * sin(((frameCount % 1800) * TAU) / 1800) + width / 2;
  circle(x, y, 20);

  if (millis() >= 500 + timer) {
    let s = new Soundwave(x, y, 0, 0);
    soundwaves.push(s);
    timer = millis();
  }

  noFill(0);
  stroke(255);
  strokeWeight(1);
  for (let i = 0; i < soundwaves.length; i++) {
    soundwaves[i].show();
    soundwaves[i].update();
  }
}

class Soundwave {
  constructor(x, y, r, e) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.e = e;
  }

  update() {
    this.e += 8;
    this.r = this.e;
  }

  show() {
    stroke(255);
    strokeWeight(2);
    noFill();
    circle(this.x, this.y, this.r);
  }
}

2 Likes

meanwhile I experimented with WEBGL to animate everything in 3D
Now I run into two problems here. One is not WEBGL specific.

The none WEBGL specific issue: In line 52:
I wish to splice out the oldest (highest radius) soundwave from the array every 2 seconds. But it does not seem to work correctly and causes a hickup in the animation.

The problem that occured with WEBGL In line 37:
Why doesn´t this sphere move as the x position of the class Soundwaves do? They share the same x and y values

let timer = 0;
let timersplice = 0;

let soundwaves = [];
let period = 50;
let y = 0;
let x = 0;
let angle1 = 0;
let objperiod = 0.012;

function setup() {
  createCanvas(1000, 1000, WEBGL);
}

function draw() {
  background(0);

  //camera and lightning
  camera(0, 1000, 1000, 0, 0, 0, 0, 1, 0);
  let dx = 1 - width / 2;
  let dy = -1 - height / 2;
  let v = createVector(-dx, -dy, 0);
  let v2 = createVector(dx, dy, 0);
  v.normalize();
  v2.normalize();
  ambientMaterial(255);
  directionalLight(255, 0, 0, v2);
  directionalLight(0, 0, 255, v);
  ambientLight(0, 0, 50);

  //move object and sound waves x and y;
  let ampx = (0.3 * width) / 2;
  x = map(cos(angle1), -1, 1, ampx, -ampx);
  y = 0;
  angle1 += objperiod;

  //Problem: why doesn´t this sphere move as the x position of the class Soundwaves do? They share the same x and y values
  translate(x, y, 0);
  sphere(40);

  //emit soundwave every 500ms
  if (millis() >= 500 + timer) {
    let s = new Soundwave(x, y, 0, 0);
    soundwaves.push(s);
    timer = millis();
  }

  for (let i = 0; i < soundwaves.length; i++) {
    soundwaves[i].show();
    soundwaves[i].update();
  }
  //Problem: I wish to splice out the oldest (highest radius) soundwave from the array every 2 seconds. But it does not seem to work correctly and causes a hickup in the animation.
  for (let i = soundwaves.length - 1; i >= 0; i--) {
    if (millis() >= 2000 + timersplice) {
      soundwaves.splice(i, 1);
      timersplice = millis();
    }
  }
}

class Soundwave {
  constructor(x, y, radius, expand) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.expand = expand;
  }

  update() {
    this.expand += 8;
    this.radius = this.expand;
  }

  show() {
    noStroke(0);
    translate(this.x, this.y, 0);
    torus(this.radius, 10, 80, 12);
  }
}

1 Like

Now I feel I have a good version also splicing out after a number of max circles has been reached. I post the 2D version here as it´s final for me:

let soundwaves = [];
let interval;
let maxSoundwaves = 10;

let y = 0;
let x = 0;
let angle = 0;
let period = 0.006;

function setup() {
  createCanvas(windowWidth, windowHeight);
  //add soundwave on interval
  interval = setInterval(addSoundwave, 600);
}

function addSoundwave() {
  //add soundwave on interval
  let s = new Soundwave(x, y, 0, 0);
  soundwaves.push(s);
  //remove soundwave when max limit
  if (soundwaves.length >= maxSoundwaves) {
    soundwaves.splice(s, 1);
  }
}

function draw() {
  background(0);

  //move object and sound waves x;
  let ampx = 1 * width;
  x = map(sin(angle), -1, 1, 0, ampx);
  y = 500;
  angle += period;

  //object
  fill(255);
  circle(x, y, 20);

  for (let soundwave of soundwaves) {
    soundwave.show();
    soundwave.update();
  }
}

class Soundwave {
  constructor(x, y, radius, expand) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.expand = expand;
    this.speedofsound = 8;
  }

  update() {
    this.expand += this.speedofsound;
    this.radius = this.expand;
  }

  show() {
    //CIRCLE
    noFill(0);
    stroke(255);
    strokeWeight(10);
    circle(this.x, this.y, this.radius);
  }
}

I still have problems aligning torus the same way when using WEBGL but I´ll make a seperate thread on this.

1 Like

This discussion reminds me of this very old sketch: :ocean:
“Wave Trios”

1 Like