How to make grids of shapes react differently from each other?

Hi.

I’m wondering how, if possible, to have grids of shapes react to audio in different times from each other?

I have grids of Ellipses reacting to an audio input at the exact same time, but I want some Ellipses to react to the audio slower/later than the others.

EXAMPLE Timestamp (00:20). In this video, the outer particles react to the audio first, and then followed by other particles towards the center, creating a kind of ripple effect. I would love to recreate this effect in my project; only a few particles react first, then followed by another set of particles, then another, and so on.

Here’s my project made with Processing 3.5.3 and Minim. Run on Linux Manjaro XFCE 32bit with Wine.

NOTE: An external audio file is required in this project, but I couldn’t find a way to attach them here. Very sorry. You can get a free drum loop here and download it as “audio.mp3”

Code:

import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;

Minim minim;
AudioPlayer player;
AudioPlayer song;
AudioInput input;
FFT fft;

void setup() {

  size(500, 500);

  minim = new Minim(this);

//READS EXTERNAL AUDIO FILE
  song = minim.loadFile("audio.mp3", 512);
  song.loop();

  fft = new FFT(song.bufferSize(), song.sampleRate());
}

void draw() {

  background(0);
  fft.forward(song.mix);

  //FFT audio analysing
  for (int i = 0; i < 2; i++) {

    //FFT audio levels assigned as 'React'
    float react = fft.getBand(i)*5;
    
    //Making grids of Ellipses that react to the amplitude of the audio
    for (int x = 0; x < 20; x++) {
      for (int y = 0; y < 20; y++) {
        noStroke();
        
        //'Horizontal' makes one Ellipse every 30 pixels in the x axis
        //'Vertical' makes one Ellipse every 30 pixels in the y axis
        float horizontal = x*30;
        float vertical = y*30;
        
        //'React' affects the fill of Ellipses
        fill(100 + react);
        
        //'React' affects the size of Ellipses
        //Minimum size of Ellipses are set to 5x5 pixels
        ellipse(horizontal, vertical, 5 + (react/100), 5 + (react/100));
      }
    }
  }
}

Sorry in advance for any mistakes, I’m very new in Processing and programming in general.

I’m looking forward for your answer. Thank you!

1 Like

Each cell would have it individual timer with different duration random (10,289);

I always like to split display logic out into separate functions or classes if I want them to behave differently.

You still do common bulky code like this in the draw()
background(0);
fft.forward(song.mix);

but then have a function that has different parameters you can call twice or if the displays are really different two different functions/classes with their own display logic.

Ive always found frameCount quite useful for slowing things down or running things at different times

if ( frameCount % 10 == 0 ) {
// RUN SOME CODE EVERY 10 FRAMES
}

if ( frameCount % 5== 0 ) {
// RUN SOME CODE EVERY 5 FRAMES
}

Hi, thanks for your response.

This seems plausible. I’ve separated the grid into different rows, but still struggling with giving individual rows different response times.

I just learned about millis(), is there is a way to change each row’s FFT responses with this function? So the first row would react normally to the audio, but the second row would be 100milliseconds late, and the third 200milliseconds late, and so on.

I tried using millis() to draw the rows 100ms late, but it did not affect the FFT response. It was gone the first 100ms, but once it’s drawn, it continues to blink in the same rhythm as the first row.

Hi, thanks for your response.

I would love to write beautiful codes myself, but as a beginner I often prioritize “this code works” more than anything else. I’m still working on it, thanks for pointing that out.

I’ve split the grids into a few rows and each group of rows are separated into individual classes. I used frameCount to control the individual classes but it seems like it doesn’t tackle my specific problem.

frameCount does work in making ripple effects, but from the examples I’ve found, the ripples are controlled by the frame rate. I can draw the initial first row, and then the second row after 5 frames, and then the third after 10 frames, and so on. In my project however, the ripples are controlled by audio. The 1st row reacts to the audio, and then the 2nd row reacts later than the 1st, and then the 3rd row reacts later than the 2nd, and so on.

Perhaps there is a way to delay individual row’s audio response by a few frames? First row reacts normally, but the second row reacts 5 frames late, and the third row reacts 10 frames late, and so on.

Yes indeed, if it works it works, Ive written some pretty nasty code over the years!

It feels like you may need to head into the world of objects and arrays, or at least i think thats what Id do if I was trying to recreate your example.

Is that something youd like me to try and expand on, it will get a bit geeky.

So I would create a class to hold your grid items…

class ActorVector {
public float react = 0;
public PVector location=new PVector(0, 0, 0);
}

and then declare an array to hold all your grid items…

private ArrayList gridDisplayItems = new ArrayList();

In setup I would put code to populate and setup the array with data similar to how you do in draw…

for (int i = 0; i < 2; i++) {

//FFT audio levels assigned as 'React'
float react = fft.getBand(i)*5;

//Making grids of Ellipses that react to the amplitude of the audio
for (int x = 0; x < 20; x++) {
  for (int y = 0; y < 20; y++) {
   
    //'Horizontal' makes one Ellipse every 30 pixels in the x axis
    //'Vertical' makes one Ellipse every 30 pixels in the y axis       
                  
ActorVector gridItem = new ActorVector();
gridItem.location.x = x*30;
gridItem.location.y = y*30;	
    gridItem.react = 0;
gridDisplayItems.add(gridItem); 
   
  }
}

}

In your existing draw add something to use as an counter for the array index, this needs to be reset to 0 each frame

background(0);
fft.forward(song.mix);
int itemIndex = 0;

In your existing draw replace the code that draws the objects and instead update just the items you want to change that frame.
The index needs to move forward each time in your y loop…

gridDisplayItems.get(itemIndex).react = react;
itemIndex = itemIndex + 1;

then after the update loop has finished add some new code to draw the grid again with the updated values…

for (ActorVector gridItem : gridDisplayItems ) {
fill(100 + gridItem.react);
ellipse(gridItem.location.x, gridItem.location.y, 5 + (gridItem.react/100), 5 + (gridItem.react/100));
}

I got bored during lockdown and extended your code with the suggestions I made above…

import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;
import java.util.Random;

Minim minim;
AudioPlayer player;
AudioPlayer song;
AudioInput input;
FFT fft;
int updateRow = 0;
Random rand = new Random();

ArrayList<ActorVector> gridDisplayItems = new ArrayList<ActorVector>();  

void setup() {

  size(500, 500);

  minim = new Minim(this);

  //READS EXTERNAL AUDIO FILE
  song = minim.loadFile("audio.mp3", 512);
  song.loop();

  fft = new FFT(song.bufferSize(), song.sampleRate());
  
  // Setup initial array to hold are grid
  fft.forward(song.mix);  
  for (int i = 0; i < 2; i++) {    
    //Making grids of Ellipses that react to the amplitude of the audio
    for (int x = 0; x < 20; x++) {
      for (int y = 0; y < 20; y++) {                                
        ActorVector gridItem = new ActorVector();
        gridItem.location.x = x*30;
        gridItem.location.y = y*30;  
        gridItem.react = 10;
        gridDisplayItems.add(gridItem);        
      }
    }
  }
    
}

void draw() {

  background(0);
  fft.forward(song.mix);
  int itemIndex = 0;
     
  // speed FFT audio adjustments
  if ( frameCount % 5 == 0 ) { 
    //FFT audio analysing
    for (int i = 0; i < 2; i++) {
  
      //FFT audio levels assigned as 'React'
      float react = fft.getBand(i)*5;
      //System.out.println("react:" + react );
      
      //Making grids of Ellipses that react to the amplitude of the audio
      for (int x = 0; x < 20; x++) {
        for (int y = 0; y < 20; y++) {   
          
          if (updateRow==x){
            // We are updating this row set a new size and colours            
            gridDisplayItems.get(itemIndex).react = 10 + (react/10);
            
            float newColourBase = rand.nextInt(155)+100;
            gridDisplayItems.get(itemIndex).redFill = 255;
            gridDisplayItems.get(itemIndex).greenFill = newColourBase + (rand.nextInt(60) * (rand.nextBoolean() ? -1 : 1))  ;
            gridDisplayItems.get(itemIndex).blueFill = newColourBase + (rand.nextInt(60) * (rand.nextBoolean() ? -1 : 1))  ;   
            
            // Reset Y to bottom
            gridDisplayItems.get(itemIndex).location.y = y*30;
          }
          else         
          { 
            // we are not updating this row so shrink the item size
            if (gridDisplayItems.get(itemIndex).react >2)
              gridDisplayItems.get(itemIndex).react--;
            else if (gridDisplayItems.get(itemIndex).react <2) 
              gridDisplayItems.get(itemIndex).react++;
            
            // float items upwards
            gridDisplayItems.get(itemIndex).location.y-=5;
          }
          
          // move array to next item
          itemIndex = itemIndex + 1;
        }
      }        
    }
  } 
  
  // Speed of the ripple, increase to go slow
  if ( frameCount % 20 == 0 ) {     
    updateRow++;
    if (updateRow>=20)
      updateRow = 0;
  }
    
  noStroke();
  
  // Draw our array of items
  for (ActorVector gridItem : gridDisplayItems ) {                                
     fill(gridItem.redFill, gridItem.greenFill, gridItem.blueFill, 255);
     ellipse(gridItem.location.x, gridItem.location.y, gridItem.react, gridItem.react);
  } 
    
}


class ActorVector {      
  public float react = 0;  
  public PVector location=new PVector(0, 0, 0);
  
  public float redFill = 0;
  public float greenFill = 0;
  public float blueFill = 0;                     
}
1 Like