🔍

touchy chord progression in p5.js

ONLINE DEMO

notes.js

// chord generator : http://glessinger.de/joomla/media/grundwissen/Wie%20erstelle%20ich%20einen%20Septakkord.pdf
 
let maj7_filter = [4,3,4];
let m7_filter = [3,4,3];
let _7_filter = [3,3,4];
let dim7_filter = [3,3,3];
let major_filter = [4,3];
let lydian_filter = [2,2,2,1,2,1];

 
let chord_filters = [maj7_filter,m7_filter,_7_filter,dim7_filter,major_filter,lydian_filter];

let bnchrs = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];

let basenote = 0;

let current_filter = dim7_filter;

function extractNotes( _cfilter, _basenote ){

  let _arr = [];
  
  let stepi = float(basenote);
  
  _arr.push(bnchrs[stepi]); // add basic note
  
  //console.log("steppi_one:" + stepi);
  
  for(let i=0;i<_cfilter.length;i++){
  
      stepi += float(_cfilter[i]);
    
   
    if(stepi > 11){  _arr.push(bnchrs[stepi-12]); }else{
      
      _arr.push(bnchrs[stepi]);      
    }
    }
  
  // clear note array -----------
  notes = [];
  
  // fill octaves
  for(let j=0;j<3;j++){
  
      for(let k=0;k<_arr.length;k++){
        
        notes.push(_arr[k]+(j+3));
        
      }
  }
  
  drawStageBasics();
}        

sketch.js

let dragging = false;
let notes = [];
let synth;

let pad = 0;
let active_canvas;
let static_canvas;

let  has_touch = false;

function setup() {
  
 
  createCanvas(window.innerWidth, window.innerHeight);
 
  
  active_canvas = createGraphics(width,height);
  static_canvas = createGraphics(width,height);
  
   initSynth();
 
   noStroke();
  
   noSmooth();
  
  pad = height*.1;
   extractNotes( current_filter, basenote );
  
  
  has_touch = isTouchDevice();
  //console.log(has_touch)
  
  
}


function isTouchDevice(){
    return typeof window.ontouchstart !== 'undefined';
}

function drawStageBasics(){
  
   let ybit = (height*.8) /notes.length;

    
    
    static_canvas.clear();
     static_canvas.textSize(14);
  static_canvas.textAlign(CENTER,CENTER);
    static_canvas.text(current_chord_id, width/2,height*.05);
  
   for(let i=0;i<notes.length;i++){
  
      static_canvas.noStroke();
      static_canvas.fill(0,22);
      static_canvas.textSize(7);
      static_canvas.textAlign(RIGHT,CENTER);
    static_canvas.text(notes[i],pad*.8,i*ybit+ybit/2+pad );
     
    
     
     
      static_canvas.fill(111,155);
       
      static_canvas.rect(pad,i*ybit+pad,width-pad*2,1);
      //static_canvas.rect(pad,i*ybit+pad+ybit-2,width-pad*2,1);
    
   }

  
     let wbit = (width-pad*2)/bnchrs.length;
  
    // draw Bar lines -------------
    for(let i=0;i<bnchrs.length;i++){
      
       static_canvas.textAlign(CENTER);
         static_canvas.text( bnchrs[i], wbit*i+pad+wbit/2,height*.95 );
      
    }
  
    // draw top selector!
   static_canvas.textSize(18);
  static_canvas.textAlign(CENTER);
   static_canvas.text( ">", width*.75,height*.05 );
  static_canvas.text( "<", width*.25,height*.05 );
  
  
  let bnbit = (width-pad*2)/12;
  static_canvas.noFill();
  static_canvas.stroke(111);
  static_canvas.ellipse(basenote*bnbit+pad+bnbit/2,height*.95,33,33);
  

}


function draw() {
  
  background(255);
  
  active_canvas.noStroke();
   active_canvas.fill(255,18);
  active_canvas.rect(0,0,width-pad*2,height-pad*2);
  image(active_canvas,pad,pad);
  
  
  image(static_canvas,0,0);
  
  if(dragging){
  
      let nid = int(( (mouseY-pad) / (height*.8))*notes.length);
      pointed_note = notes[nid];
    
      if(pointed_note != played_note){
    
        
        if(synth.activeVoices>31){return;}
         const now = Tone.now();
         synth.triggerAttackRelease(pointed_note,"8n",now);
         played_note = pointed_note;
        
         drawScale(nid);
        
      }
      
    
     let lpfilter_val = abs(sin( ((mouseX-pad) / (width-pad*2)) * PI+PI) *1000 ) +100;
    
     //console.log( lpfilter_val);
    
     filter.frequency.value =  lpfilter_val;
   // filter.detune.value  =  int((mouseX-pad)*1.1);
  
  }else{
  
      drawScale(-1);
  }
  
  
}

let filter;

function initSynth(){

  synth = new Tone.PolySynth();
  synth.polyphony  = 32;
  //synth.harmonicity.value =  2.1;
   
  console.log(synth);
  
 // synth.options.detune = 155;
  
  //synth.set({ detune: 155 });
  
   synth.options.envelope.attack = .03; 
   synth.options.envelope.release = .04;
   synth.options.envelope.sustain = .5;
   synth.options.envelope.decay = .1;
  
  
   synth.options.oscillator.modulationType = "sine";
  synth.options.oscillator.harmonicity = .5;
  synth.options.oscillator.type = "sine2";
   synth.options.oscillator.partials = [.9,.701 ];
  
  
  // synth.options.oscillator.type = "fmtriangle";
   synth.volume.value = -16;
  
  //console.log(synth);
   //synth.oscillator.type = "triangle";
    
 // synth.oscillator.count = 18;
  // console.log(synth.oscillator.type);

  const now = Tone.now();
 // synth.triggerAttackRelease("C4", "3n", now);
  
   const feedbackDelay = new Tone.FeedbackDelay(0.13, 0.3) ;
  
  const freeverb = new Tone.Freeverb();
freeverb.dampening = 1000;
  freeverb.wet.value = .3;
   freeverb.roomSize.value = .8;
  
  const chorus = new Tone.Chorus(3.5, 52.5, 0.9);
  
  filter = new Tone.Filter(1500, "bandpass");
    
   synth.chain( filter,   freeverb,chorus, Tone.Destination);
  
}


let pointed_note = "";
let played_note = "";

 


// ------------------------------------

function down(){

    if(mouseX>pad && mouseY>pad){
  dragging = true;
  }
  
  // change scale ------------
  if(mouseY<pad*.7){
      if(mouseX>width/2){

        current_chord_id++;
        if( current_chord_id>chord_filters.length-1){ current_chord_id = 0; }

      }else{

          current_chord_id--;
          if( current_chord_id<0){ current_chord_id = chord_filters.length-1; }
      }
    skipChordFilter(current_chord_id);
  }
  
  // change basenote
  if(mouseY>height*.92){
  
    let npickid = int( ((mouseX-pad)/ (width-pad*2) ) * bnchrs.length );
   
    basenote = npickid; 
    //console.log(basenote);
    extractNotes( current_filter, basenote );
    
    
    
  }

}

// ------------------------------------


function up(){

   dragging = false;
   pointed_note = "";
   played_note = "";
  

}

// ------------------------------------

function mousePressed() {  if( !has_touch){ down(); }}
function mouseReleased() { if( !has_touch){   up(); }}

function touchStarted() {  if( has_touch){ down();}}
function touchEnded()  {  if( has_touch){ up();}}

// ------------------------------------


function drawScale(_tnid){

  let ybit = (height*.8) /notes.length;
  
  pad = height*.1;
  
  for(let i=0;i<notes.length;i++){
  
    if(i==_tnid){
      
       active_canvas.noStroke();
       active_canvas.fill("#4DC3FF");
       active_canvas.rect(0,ybit*i,width-pad*2,ybit);
    } 
  
  }

}

function setBaseNote(_e){
  basenote = _e.value;
  extractNotes( current_filter, basenote );
}


 


let current_chord_id = 0;

function setChordFilter(_e){

// console.log("change filter: " + chord_filters[ _e.value]);
  
  current_chord_id = _e.value;
  current_filter = chord_filters[ _e.value];
  extractNotes( current_filter, basenote );
}

function skipChordFilter(_v){

  current_filter = chord_filters[_v];
  extractNotes( current_filter, basenote );
}

<!DOCTYPE html>
<html lang="en">
  <head>
  <head>
    
     <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.12/Tone.js" integrity="sha512-MyXAzMk3sw/i85erXXKd2+z2fzlmKD15TzKKwZd541ifNwUQ8Z73CfAwuyIPc0p5bL7xEA3u/l+/V0K9IyPbCg==" crossorigin="anonymous"></script>

                  <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
   
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>

    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>

  </head>
  <body>
    <script src="notes.js"></script>
    <script src="sketch.js"></script>
    
    <!--
    
<select id = "basenote_list" onchange = "setBaseNote(this)" >  
  <option value="0" >C</option>  
  <option value="1" >C#</option>  
  <option value="2" >D</option>  
  <option value="3" >D#</option>  
  <option value="4" >E</option>  
</select>  
    -->
    <!--
<select id = "chordfilter_list" onchange = "setChordFilter(this)" >  
  <option value="0" >Maj7</option>  
  <option value="1" >m7</option>  
  <option value="2" >7</option>  
  <option value="3" >Dim7</option>  
  <option value="4" >major</option>  
</select> 
    -->
    
    
    
  </body>
</html>