function fft_cubes()
{
    this.start_time = 0;
    this.end_time = 0;

	  this.camera, this.scene, this.renderer, this.composer;
    this.geometry, this.pgeometry, this.pmaterial, this.material, this.material_depth;     
    this.materials = [];
    this.object, this.mesh = [];
    this.nobjects;     
    this.postprocessing = {};
    this.light;
    this.barIdx = 0;
    this.px, this.py, this.pz;
    this.particles, this.color, this.colors, this.positions;
    this.aBloomFactor = 1.5, this.aBloomStrength = 1.0; // Adjust parameters as you want
    this.aEffectCopy;
    this.aEffectBloom;
    this.aRenderModel;
    this.mirror = null;
    this.size = 80;
    this.scale = 1;
    
    this.dotScreenEffect, this.rgbShiftEffect, this.edgeEffect, this.edgeEffect2;
    this.parameters = { color: 0x000000, wireframe: false, shading: THREE.FlatShading };
    this.webaudio = new WebAudio();
    this.sound;
    this.sound = webaudio.createSound().load('audio/morph.mp3', 
      function(sound)
      {
        sound.loop(false).play(0,35);
       });

    this.init = function(renderer, camera)
    {      
    
      this.renderer = renderer;            
      // this.camera = camera;

      this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
      this.camera.position.z = 1500;
      this.camera.position.x = 100;

      this.scene = new THREE.Scene();


      this.object = new THREE.Object3D();
      this.scene.add(this.object);

      this.light = new THREE.SpotLight(0xffffff, 1.25);
      this.light.position.set(-500, 900, 600);
      this.light.target.position.set( 100, 0, 100);
      this.light.castShadow = true;
      
      this.scene.add(this.light);

      // Cubes ----------------------------------------------------------------------
      var xg = 25, yg = 25; zg = 1;
      this.nobjects = xg*yg*zg;
      var i = 0;
      var s = 60;               

      for (var x = 0; x<xg; x++)
        for (var y = 0; y<yg; y++)
          for (var z = 0; z<zg; z++)
          {
              
            if ((i&301) == 0)
              this.materials[i] = new THREE.MeshPhongMaterial( { color: 0xFF0000, shading: THREE.FlatShading } );
            else if ((i&350) == 0)
              this.materials[i] = new THREE.MeshPhongMaterial( { color: 0x808080, shading: THREE.FlatShading } );
            else
              this.materials[i] = new THREE.MeshPhongMaterial( { color: 0xFFFFFF, shading: THREE.FlatShading } );

            this.geometry = new THREE.CubeGeometry(this.size, this.size, this.size);

            this.mesh[i] = new THREE.Mesh(this.geometry, this.materials[i]);
            this.mesh[i].castShadow = true;
            this.mesh[i].receiveShadow = true;
            this.renderer.initMaterial(this.materials[i], this.scene.__lights, this.scene.fog, this.mesh[i]);

            px = 100 * (x - xg/2);
            py = 100 * (y - yg/2);
            pz = 100 * (z - zg/2);

            this.mesh[i].position.set(px, py, pz);
              
            this.mesh[i].matrixAutoUpdate = false;
            this.mesh[i].updateMatrix();

            if ((i&27) != 0)
            {
              this.mesh[i].exist = false;              
              this.object.add(this.mesh[i]);
            }
            else
            {
              this.mesh[i].exist = true;              
            }

            i++;
          }   

        this.object.rotation.x = 90;    
        // Fin cubes -----------------------------------------------------------------

        this.aEffectBloom = new THREE.BloomPass(this.aBloomStrength, this.aBloomFactor * 25.0, this.aBloomFactor * 4.0, 512 );
       
        // Create the post-effect system
        this.aEffectCopy = new THREE.ShaderPass(THREE.CopyShader);
        this.aEffectCopy.renderToScreen = true;    
        this.aRenderModel = new THREE.RenderPass(this.scene, this.camera);               
       
        // Create the rendering pass
        this.composer = new THREE.EffectComposer(this.renderer);
        this.composer.addPass(this.aRenderModel);
        this.composer.addPass(this.aEffectBloom);
        this.composer.addPass(this.aEffectCopy);          


    };

    this.setupRenderer = function(){
    
      this.camera.position.z = 1500;
      this.camera.position.x = 100;

      this.renderer.shadowMapEnabled = true;
      this.renderer.shadowMapSoft = true;

      this.renderer.shadowCameraNear = 3;
      this.renderer.shadowCameraFov = 50;

      this.renderer.shadowMapBias = 0.0039;
      this.renderer.shadowMapDarkness = 0.5;
      this.renderer.shadowMapWidth = 512;
      this.renderer.shadowMapHeight = 512;
      
    }


    this.render = function(demoFrame)
    {
    	  var time = Date.now() * 0.00005;        

        var m = Math.floor(Math.random()*this.nobjects);

        this.object.rotation.z += demoFrame; 

        // camera.position.z -= Math.abs(0.5*Math.sin(time));
        
        var nBarHalf = Math.ceil(41/2)
        var histo = this.sound.makeHistogram(nBarHalf);
        var histoIdx = this.barIdx < nBarHalf ? nBarHalf-1-this.barIdx : this.barIdx - nBarHalf;       

        this.scale = 1;

        if (histo[10] > 120)  
        { 
          this.scale = 20;                             
        } 

        for (u=0; u<this.mesh.length; u++)
        {
          if (this.mesh[u].scale.z > 1)
          {
            this.mesh[u].scale.z -= 0.5;
          }          
          this.mesh[u].updateMatrix();
        }

        this.mesh[m].scale.z = this.scale;
        
        this.mesh[m].updateMatrix();

        if (m+1 < this.mesh.length)
        {
          this.mesh[m+1].scale.z = this.scale/3;
          this.mesh[m+1].updateMatrix();
        }
        if (m-1 > 0)          
        {
          this.mesh[m-1].scale.z = this.scale/3;
          this.mesh[m-1].updateMatrix();
        }
        if (m+25 < this.mesh.length)
        {
          this.mesh[m+25].scale.z = this.scale/3;
          this.mesh[m+25].updateMatrix();
        }
        if (m-25 > 0)
        {
          this.mesh[m-25].scale.z = this.scale/3;
          this.mesh[m-25].updateMatrix();
        }       

        this.renderer.clear();
        this.renderer.autoClear = false;
        this.composer.render();
    }
}