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

    var overlay3;
    var overlay2;
    var fader;
    
	  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.nBarHalf;
    this.histo;
    this.histoIdx;

    this.size = 80;
    this.scale = 40;
    this.accum = 0;
    
        
    //this.dotScreenEffect, this.rgbShiftEffect, this.edgeEffect, this.edgeEffect2;
    this.parameters = { color: 0x000000, wireframe: false, shading: THREE.FlatShading };
    
    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 = 17, yg = 17; zg = 1;
      this.nobjects = xg*yg*zg;
      var i = 0;
      var s = 20;               

      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 = 85 * (x - xg/2);
            py = 85 * (y - yg/2);
            pz = 85 * (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;    
        
      // Create end Overlay ----------------------------------------------------------------------------------------
      var map2 = THREE.ImageUtils.loadTexture( 'images/end.png' );
			map2.wrapS = map2.wrapT = THREE.RepeatWrapping;
      map2.repeat.set( 1, 1 );

			var material2 = new THREE.MeshLambertMaterial( { 
        color: 0xff0000,
        specular: 0xffffff,
        ambient: 0xffffff,
        emissive: 0xaaaaaa,
        map: map2, 
        side: THREE.FrontSide,  
        transparent: true,
        depthTest: false
      } );
      material2.opacity = 1.0;
      
      overlay2 = new THREE.Mesh( new THREE.PlaneGeometry( 2300, 2300, 1, 1 ), material2 );
      overlay2.position.set( 0, 0, 10 );
      
      this.scene.add( overlay2 );    

      // Create Circle Overlay ----------------------------------------------------------------------------------------
      var map3 = THREE.ImageUtils.loadTexture( 'images/circle3.png' );
			map3.wrapS = map3.wrapT = THREE.RepeatWrapping;
      map3.repeat.set( 1, 1 );

			var material3 = new THREE.MeshLambertMaterial( { 
        color: 0xffffff, 
        map: map3, 
        side: THREE.FrontSide,  
        transparent: true,
        depthTest: false
      } );
      material3.opacity = 0.8;
      
      overlay3 = new THREE.Mesh( new THREE.PlaneGeometry( 1500, 1500, 1, 1 ), material3 );
      overlay3.position.set( 0, 0, -10 );
      
      this.scene.add( overlay3 );    

      // Create Fader Overlay ----------------------------------------------------------------------------------------

			var faderMat = new THREE.MeshLambertMaterial( { 
        color: 0x000000, 
        side: THREE.FrontSide,  
        transparent: true,
        depthTest: false
      } );
      faderMat.opacity = 0.0;
      
      fader = new THREE.Mesh( new THREE.PlaneGeometry( 4000, 2300, 1, 1 ), faderMat );
      fader.position.set( 0, 0, 0 );
      
      this.scene.add( fader );    
      
              
        // 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);    */  

        // 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.renderer.setClearColor( 0x222222, 1.0 );
      this.camera.position.z = 1250;
      this.camera.position.x = 100;  
      
      this.renderer.autoClear  = false;    
      this.renderer.shadowMapEnabled = 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.preRenderer = function(){
        this.composer.render();    
    }
    
    this.render = function(demoFrame)
    {
    	  var time = Date.now() * 0.00005;        
        //this.accum += demoFrame;
        
        var m = Math.floor(Math.random()*this.nobjects);

        this.object.rotation.z += 1.0*Math.sin(demoFrame); 

        this.camera.position.z += 500*Math.cos(time*10);
        
        overlay2.position.z = 500*Math.cos(time*10);
        fader.position.z = 500*Math.cos(time*10);
        //overlay2.material.opacity += demoFrame;
        
        this.nBarHalf = Math.ceil(41/2)
        this.histo = sound.makeHistogram(this.nBarHalf);
        this.histoIdx = this.barIdx < this.nBarHalf ? this.nBarHalf-1-this.barIdx : this.barIdx - this.nBarHalf;       

        this.scale = 1;

        if (this.histo[10] > 70)  
        { 
          this.scale = 8;                             
        } 

        for (u=0; u<this.mesh.length; u++)
        {
          if (this.mesh[u].scale.z > 1)
          {
            this.mesh[u].scale.z -= demoFrame*50;
          }          
          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();
        }       

        // Start black fader
        if(clock.getElapsedTime() > 165)
          fader.material.opacity += demoFrame*0.5;
          //logo.position.z = 250 - 250.0*Math.sin(clock.getElapsedTime()*0.3);


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