• fullscreen
  • color_space.pde
  • import traer.physics.*;
    import beads.*;
    
    
    AudioContext ac;
    ArrayList carrierFreqs;
    ArrayList modFreqRatios;
    ArrayList panners;
    
    Particle selectedParticle = null;
    
    boolean initialized = false;
    boolean frozen = false;
    
    int frameMousePressed =0;
    
    ParticleSystem ps = new ParticleSystem();
    
    void setup() {
      background(0);
      ac = new AudioContext();
      size(400,400);
      colorMode(HSB,100);
      background(0);
      smooth();
      frameRate(30);
        strokeWeight(.5);
        stroke(100);  
      
      carrierFreqs = new ArrayList();
      modFreqRatios = new ArrayList();
      panners = new ArrayList();
        
    }
    
    void drawParticles() {
      for(int i=0; i<ps.numberOfParticles(); i++) {
        Particle p = ps.getParticle(i);
        float mass = p.mass();
        noStroke();
        // HSB based on X,Y,Z
        float br = map(sin(frameCount/mass + PI), -1, 1, 60, 95);
        float ra = map(sin(frameCount/mass), -1, 1, mass, mass * 2);
        fill( color( p.position().x(), p.position().y(), br ) , 15);
        ellipse(width * (p.position().x() / 100),  height * (p.position().y() / 100), ra, ra);
        
        if(p.isFixed()) {
          fill(0,50);
          ellipse(width * (p.position().x() / 100),  height * (p.position().y() / 100), 2,2);
        }
        
        Glide cf = (Glide)carrierFreqs.get(i);
        Glide mfr = (Glide)modFreqRatios.get(i);
        PanMonoToStereo pan = (PanMonoToStereo)panners.get(i);
        
        /* update values */
        cf.setValue(map(width * (p.position().x()/100),0,width,30,600));//((float)p.position().x() / width * 500 + 50) % 20);
        mfr.setValue(map(height * (p.position().y()/100),0,height,.1,2));//(1 - (float)p.position().y() / height) * 10 + 0.1);
        pan.getPanEnvelope().setValue(map(p.position().x(),0,100,1,0));
      }
    
    }
    
    void drawSprings() {
      stroke(25);
      strokeWeight(.5);
      for(int i=0; i<ps.numberOfSprings(); i++) {
        Spring s = ps.getSpring(i);
        Particle p1 = s.getOneEnd();
        Particle p2 = s.getTheOtherEnd();
        line(width * (p1.position().x() / 100), height * (p1.position().y() /100),
             width * (p2.position().x() / 100), height * (p2.position().y() /100));
      }
    }
    
    void drawGas() {
      scale(1.5);
      fill(100,1);
      beginShape();
      for(int i=0; i<ps.numberOfParticles()-1; i++) {
        Particle p0 = ps.getParticle(i);
        Particle p1 = ps.getParticle(i+1);
        curveVertex(width*(p0.position().x()/100), height *( p0.position().y()/100));
      }
      endShape(CLOSE);
    }
    
    void drawThread() {
      int nop = ps.numberOfParticles();
      if(nop < 4) return;
      for(int i=0; i<nop; i++) {
    
        Particle p0 = ps.getParticle(i%nop);
        Particle p1 = ps.getParticle((i+1)%nop);
        Particle p2 = ps.getParticle((i+2)%nop);
        Particle p3 = ps.getParticle((i+3)%nop);
    
        color c0 = color( p1.position().x(), p1.position().y(), p1.position().z() );
        color c1 = color( p2.position().x(), p2.position().y(), p2.position().z() );
        color c2 = lerpColor(c0, c1, noise(frameCount)); // interpolate between the two colors
        c2 = color(hue(c2), saturation(c2)/2, brightness(c2));
    
        
        strokeWeight(1);
        stroke(c2, 50);
        noFill();
        curve(width * (p0.position().x() /100), height * (p0.position().y()/100),
              width * (p1.position().x() /100), height * (p1.position().y()/100),
              width * (p2.position().x() /100), height * (p2.position().y()/100),
              width * (p3.position().x() /100), height * (p3.position().y()/100));
      }
       
    }
    
    void draw() {
      ///////// erase old
      fill(0,.1);
      noStroke();
      rect(0,0,width,height);
      filter(BLUR, 1);
      
    //  drawSprings();
    //  drawGas();
      drawThread();
      drawParticles();
    
      if(!frozen) ps.tick();
      //checkBounds();
      curveTightness( map ( noise(frameCount*.001), 0, 1, -5, 5));
    
    }
    
    float wrap(float val, float mn, float mx) {
      return max(mn, min(val, mx));
    }
    
    void checkBounds() {
      for(int i=0; i<ps.numberOfParticles(); i++) {
        Particle p = ps.getParticle(i);
        float x = p.position().x();
        float y = p.position().y();
        float z = p.position().z();
        //x = wrap(x, 0, 100);
        //y = wrap(y, 0, 100);
        x = max(0, min(x, 100));
        y = max(0, min(y, 100));
        z = max(0, min(z, 100));
        p.position().set(x,y,z);
      }
    }
    
    /* only called on particle add, so using slow/naive method */
    Particle findNearestParticle(float x, float y) {
      float closestDistance = 1000000;
      int closestIndex = 0;
      for(int i=0; i < ps.numberOfParticles(); i++) {
        Particle p = ps.getParticle(i);
        float d = dist(width * (p.position().x()/100), height*(p.position().y()/100), x, y);
        if(d < closestDistance) {
          closestDistance = d;
          closestIndex = i;
        }
      }
      return ps.getParticle(closestIndex);
    }
    
    /*
    ********************************************
    * INTERACTIVE FUNCTIONS
    *
    */
    
    void mousePressed() {
      frameMousePressed = frameCount;
      if(key == CODED) {
        if(keyCode == SHIFT) {
          Particle p = findNearestParticle(mouseX, mouseY);
          if(p.isFixed()) p.makeFree();
          else p.makeFixed();
        }
        else if(keyCode == ALT) {
          selectedParticle = findNearestParticle(mouseX, mouseY);
        }
      }
    }
    
    void mouseReleased() {
      if(key == CODED) {
         if(keyCode == SHIFT) return; // we were freezing a point, don't add a new one
         else if(keyCode == ALT) {
           if(selectedParticle != null) {
              selectedParticle.position().set(((mouseX*1.0)/width)*100, ((mouseY*1.0)/height)*100, 70);
           }
         }
      }
      else {  
        addParticle(mouseX,mouseY,frameCount - frameMousePressed + 1);
        createNoisemaker(mouseX,mouseY,frameCount - frameMousePressed + 1);  
      }
    }
    
    void addParticle(int x, int y, int sz) {
      Particle p2=null;
      if(ps.numberOfParticles() >= 1) {
        p2 = findNearestParticle(((x*1.0)/width)*100, ((y*1.0)/height)*100);
      }
      Particle p0 = ps.makeParticle(sz, ((x*1.0)/width)*100, ((y*1.0)/height)*100, 70);
    
      
      // adding repulsions to all other particles 
      for(int i=0; i<ps.numberOfParticles(); i++) {
        Particle p1 = ps.getParticle(i);
        if(p0 != p1) {
          ps.makeAttraction(p0, p1, -3, 0);
        }
      }
      
      
      if(ps.numberOfParticles() >= 2) {
        // strength, dampening, rest length
        ps.makeSpring(p2, p0, .1, .1, 25);
      }
      
    }
    
    
    
    void createNoisemaker(float x, float y, float sz) {
      sz = max(3, min(sz,100));
      Glide cf = new Glide(ac, 500);
      carrierFreqs.add(cf);  // save a reference for later
      Glide mfr = new Glide(ac, 1);
      modFreqRatios.add(mfr);
       
      Function modFreq = new Function(cf, mfr) {
        public float calculate() {
          return x[0] * x[1];
        }
      };
      WavePlayer freqModulator = new WavePlayer(ac, modFreq, new SineBuffer().getDefault());
    
      Function carrierMod = new Function(freqModulator, cf) {
        public float calculate() {
          return x[0] * 300.0 + x[1];    
        }
      };
      
      WavePlayer wp = new WavePlayer(ac, carrierMod, new SineBuffer().getDefault());
      Gain g = new Gain(ac, 1, map(sz,0,100,0,1)); // more massive particles are louder
      
      PanMonoToStereo pan = new PanMonoToStereo(ac, new Envelope(ac, 0.5)); // not working?
      panners.add(pan);
      
      g.addInput(wp);
      pan.addInput(g);
      
      ac.out.addInput(pan);
      if(! initialized) {
        ac.start();  // only start it once
        initialized = true;
        println("started");
      }
    }
    
    
    void keyPressed() {
      if(key == 'f') {
        frozen = !frozen;
      }
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    This sketch is saved as a draft and it is not published on the homepage and browse page.

    license

    advertisement

    Luke Loeffler

    Sound of Color

    Add to Faves Me Likey@! 32
    You must login/register to add this sketch to your favorites.

    A simple FM synthesizer which uses the individual positions of objects in a mesh to determine the pitch and tonal characteristics of each oscillator. Driven by a physics simulation, the sound-producing objects oscillate and move about eachother attempting to reach equilibrium.

    Click to add a sound generator. The longer you hold the left mouse button, the larger the object. Press the 'f' key on the key to freeze objects; shift-click to freeze an individual; opt-drag to move.

    made with traer physics and the beads sound lib. http://lukeloeffler.com

    that's really cool!
    eduardo jimenez
    17 Nov 2009
    yeah! nice work
    Rui Gil
    2 Dec 2009
    This is great !
    Dave Phillips
    24 Nov 2010
    I'd like to run it as a sketch within Processing, but I receive an error regarding PanMonoToStereo. Is it available in the Processing version of the Beads library ? Or can you suggest a work-around ?
    Luke Loeffler
    25 Nov 2010
    I'm not sure as I haven't touched this in a while. Perhaps the libraries have changed since I wrote it--Beads was under pretty heavy development when I was using the library. You tried using the beads library distributed with the applet?
    Dave Phillips
    26 Nov 2010
    Thanks, Luke, that fixed the trouble. Nice sketch, I'll be studying this one. :)
    lineo
    29 Oct 2012
    great stuff! I wonder what's the best way to "reset" to the initial state.
    You need to login/register to comment.