Siri style waveform design with p5 sound

How would I go about creating an animation similar to this?:

https://dribbble.com/shots/5401314-Voice-interface-design

1 Like

Hi bob, welcome to the forums!

I can see what you’re talking about, and I don’t think it’s gonna take too long to piece together, but it all really depends on how much experience you have with js, and p5.sound.

This might be a good start:

If you’re looking for just a filler animation, you might be able to just throw some colored sin waves together.

If you’re looking for something that actually responds to an input, you’ll want to get familiar with the AudioIn object:

1 Like

Thanks Tony. I found this in the meantime and managed to connect it to my audio file instead of a mic input:

https://therewasaguy.github.io/p5-music-viz/demos/05_fft_scaleOneThirdOctave/

Source code here:

Very much on the same lines of the video you shared, but with curved vertexes, smoothed points, and octave splitting.

I’m pretty experienced with JS, but this library is very new to me. Haven’t worked with anything similar either. I think I could maybe hack around this one third octave idea, but I’m not sure exactly which bits I need to be altering.

I assume I would probably want to split the frequency range into different sections to create multiple waves, and then using them to draw sin waves as you say, instead of a curve vertex?

1 Like

That sounds like a great idea! I’ve actually never made anything with the sound library either but, on the graphics side, that sounds like it should achieve what you’re looking for. Unless this has some deadline, I would just experiment and see what looks best haha.

@tony well this is a lot of fun!

Although I’ve found myself at something of a block. Here’s what I’ve got so far:

Screenshot 2020-06-04 at 00.19.58

Using this code:

sk.colorMode(sk.RGB);
    let from = sk.color(218, 165, 32);
    let to = sk.color(72, 61, 139);
    let a = 0.0;
    let inc = sk.TWO_PI / 40;
    for (let i = 0; i < sk.displayWidth; i++) {
      let inter = sk.map(0, sk.displayWidth, i, i, 1);
      sk.strokeWeight(10);
      sk.stroke(sk.lerpColor(from, to, inter));
      sk.line(i * 10, 150, i * 10, 150 + sk.sin(a) * 100.0);
      a = a + inc;
    }

What i really want to do is instead of having a line at each point, I want one smooth line that follows the data points. And the gradient should apply to the vertical axis, with say purple at the tops and bottoms, blending towards orange in the middle?

Do you have any experience with something like this that might put me in the right direction?

I also tried using a vertex shape, but the fill isn’t working as i’d like

Screenshot 2020-06-04 at 00.32.42

Vertex alignment figured out. But still absolutely no idea how to get the gradient to work
Screenshot 2020-06-04 at 00.42.41

sk.beginShape();
    sk.vertex(0, 200);

    let a = 0.0;
    let inc = sk.TWO_PI / 25;
    for (let i = 0; i < sk.displayWidth; i++) {
      var y = sk.map(sk.sin(a), -1, 1, 150, 250);
      sk.vertex(i * 10, y);
      a = a + inc;
    }

    sk.vertex(width, 200);
    sk.endShape();

Neat!

This is unfamiliar territory, but don’t worry, you’re not the first to play with gradients. I found this thread from a couple years back:

Maybe you can find something useful from this?

Ended up sacking off the gradient idea for something a little more stylized. I was wanting to simply do a transparency gradient anyway so this did a nice similar-ish effect.

RPReplay-Final1591310444

Here’s the code if anyone is interested in future:

let s = sk => {
  const width = sk.windowWidth;
  const height = 300;

  sk.setup = () => {
    context = sk.getAudioContext();
    const sound = sk.loadSound('file.mp3');
    var canvas = sk.createCanvas(width, height);
    canvas.parent('canvas');
    sk.noFill();
    fft = new p5.FFT(0.8, 1024);
    fft.setInput(sound);
    sound.play();
  };

  sk.draw = () => {
    sk.background(255);
    sk.colorMode(sk.RGB);
    sk.strokeWeight(3);

    fft.analyze();
    var bass = fft.getEnergy('bass', 'lowMid');
    var mid = fft.getEnergy('mid', 'highMid');
    var treble = fft.getEnergy('treble');

    var inc = sk.TWO_PI / 100;
    var multipliers = [0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35];

    var waves = [
      {
        range: bass,
        amplitude: 0.5,
        from: sk.color(228, 229, 248, 255),
        to: sk.color(228, 229, 248, 0),
      },
      {
        range: mid,
        offset: 400,
        from: sk.color(195, 192, 240, 255),
        to: sk.color(195, 192, 240, 0),
      },
      {
        range: treble,
        offset: 900,
        from: sk.color(180, 213, 255, 255),
        to: sk.color(180, 213, 255, 0),
      },
    ];

    waves.map(({range, offset = 1, amplitude = 1, from, to}) => {
      for (var multiplier of multipliers) {
        sk.beginShape();
        sk.curveVertex(width, height / 2);
        let a = 0.0;
        var energy = sk.map(range, 0, 255, 0, amplitude, true);
        sk.stroke(sk.lerpColor(from, to, multiplier - 0.1));
        for (let i = 0; i < sk.displayWidth; i++) {
          var y = sk.sin(a) * multiplier * (height * energy) + height / 2;
          sk.curveVertex(i * 10 - multiplier * offset - width, y);
          a = a + inc;
        }
        sk.curveVertex(width, height / 2);
        sk.endShape();
      }
    });
  };
};

const P5 = new p5(s);

Really love how intuitive this library is. I initially picked up for a quick fix, but I can see myself experimenting loads more with this!

3 Likes