< — click on — >
the Stage Is Alive ()
< — & let the dance begin — >

performance score aka script part of Choreographies of the Circle & Other Geometries a research project by Joana Chicau and Renick Bell, supported by Stimulerings Fonds (2020/21)

source code available under The (Cooperative) Non-Violent Public License.


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

// score aka script for The Stage Is (a)Live 

// comissioned by Gnration — 2021

// research project supported by Stimulerings Fonds — 2020/2021

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


// geometries data structures and basic functions

class Performance {
    constructor() {
        this.dancers = {};
        this.rhythms = {};
        this.vocabulary = {};
        }
    dancerNames() {return Object.keys(this.dancers)}
    rhythmNames() {return Object.keys(this.rhythms)}
    vocabularyList() {return Object.keys(this.vocabulary)}
    newdancer(dancer) {
        console.log(`adding a new dancer called ${dancer}... `);
        this.dancers[dancer] = new Dancer()
        }
}

class Dancer {
    constructor() {
        this.part = undefined;
        this.rhythm = undefined;
        this.vocabulary = undefined;
        this.output = undefined;
        this.args = [];
        }
}

// geometries functions to be made into methods of the classes above, most likely

function startAll (transportTime) {
    dNames = Object.keys(p.dancers);
    dNames.forEach(x => p.dancers[x].part.start(transportTime)) 
}

function stopAll (transportTime) {
    dNames = Object.keys(p.dancers);
    dNames.forEach(x => p.dancers[x].part.stop(transportTime)) 
}

function partToggle (p) {
    if (p.state == "stopped") 
    {p.start(inNMeasures(2))}
    else {p.stop(inNMeasures(2))}
}

function changeRhythmAndArgs (dancer, rhythm, args) {
    let events = timesPlusArgs(rhythm,args);
    let targetPart = p.dancers[dancer].part;
    replaceEventsWith2(targetPart,events)
}

function changeVocab (dancer,vocab) {
    let targetPart = p.dancers[dancer].part;
    targetPart.callback = vocab
    }




//--------------------------------------------------------------------------
// joana's constants

let x = document.querySelector(".stage01");
let y = document.querySelector(".stage02");
let z = document.querySelector(".stage03");
let h = document.querySelector("html");



//--------------------------------------------------------------------------
// algorithmic data tools
// often to be used for generation of rhythms


// import Timo Hoogland's Total Serialism library

const t = TotalSerialism;

// for lerp of floats use t.t.Generative.spreadInclusiveFloat

let pick = inputArray => inputArray[Math.round((inputArray.length - 1) * Math.random())];

let pickN = (n,inputArray) => {
        let a = new Array(n);
        a.fill(0,0,n);
        let out = [];
        a.forEach(i => out.push(pick(inputArray)));
        return out }

numArray = function (start,end) {
    let output = [];
    for(i = start; i <= end; i++) {output.push(i)};
    return output
}

function low2HighSort (inputArray) { return inputArray.sort((a, b) => a - b)}

function high2LowSort (inputArray) { return inputArray.sort((a, b) => b - a)}

function takeN (inputArray, n) {
    let outputArray = [];
    for (let i = 0; i < n; i++)
    { outputArray.push(inputArray[i%(inputArray.length)])};
    return outputArray
}


//--------------------------------------------------------------------------
// tone.js convenience functions

function beatToTransport (bs) { return "0:"+ bs + ":0" }

function transportToEvent (x) { return {time: x}}

function beatsToEvents (bs) {return bs.map(x => transportToEvent(beatToTransport(x)))}

function replaceEventsWith (part, inputArray) {
    part.clear(); 
    inputArray.map(beatToTransport).map(x => transportToEvent(x)).forEach(x => part.add(x))
    return
}

function replaceEventsWith2 (part, inputArray) {
    part.clear(); 
    inputArray.forEach(x => part.add(x))
    return
}

function transportToBeat (t) {
    ts = t.split(':').map(parseFloat);
    beatsFromMeasures = ts[0] * 4;
    beats = ts[1];
    beatsFrom16s = ts[2]/4;
    return beatsFromMeasures + beats + beatsFrom16s 
}

function inNMeasures (n) {
    currentPosition = Tone.Transport.position;
    ts = currentPosition.split(':');
    currentMeasure = parseFloat(ts[0]);
    return (currentMeasure + n) + ":0:0"
}

// warning: this is often too soon for Tone to process without an error 
// calculation takes too long, 2 or more measures may be necessary
// if so, use functions above

function nextMeasure () {return inNMeasures(1)}

function timesPlusArgs (times,args) {
    events = beatsToEvents (times);
    argsB = takeN (args,events.length);
    argsC = argsB.map(x => {return {args: x}});   
    return events.map((x,i) => Object.assign(x,argsC[i]))
}

function randomLoopPoints (p, minRange, maxRange) {
   let range = (maxRange - minRange) * Math.random();
   let bufDur = p._buffer.duration;
   let startPoint = bufDur * Math.random();
   let endPoint = undefined;
   if (startPoint + range > bufDur) {startPoint = bufDur - minRange; endPoint = bufDur} else {endPoint = startPoint + range};
   p.stop(); p.loopStart = startPoint; p.loopEnd = endPoint; p.start();
   return {start: startPoint, end:endPoint}
} 

function retriggerLoop (p, loopPointsObject) {
p.stop(); p.loopStart = loopPointsObject.start; p.loopEnd = loopPointsObject.end; p.start();
}



//--------------------------------------------------------------------------
// initiate storage of musical data
// and populate it with initial data


const comp = new Tone.Compressor(-30, 3).toDestination();

let m = {
      args: {}
    , motifs: {}
    , rhythms: {}
    , sequences: {}
    , synths: {}
    , samples: {}
    , timespans: {}
}

m.motifs.riffA = ["A2","B2","E2","E#2","G3","A3","B3","E3","E#3","A4","B4","E4","E#4","G4","A5"];
m.motifs.riffA2 = ["A1","B1","E1","E#1","G1","A2","B2","E2","E#2","G3","A3"];
m.motifs.riffB = ["A2","B2","E2","A3","E3"];
m.motifs.riffC = ["A3","B3","E3","E#3","A4","B4","E4","E#4","G4","A5","B5","E5"];


//var verb = new Tone.Reverb().toMaster();

var verb = new Tone.JCReverb(0.4).toMaster();

m.synths.synth03 = new Tone.Sampler({
  urls: {
    D4: "circuloSubtleDrum03-D4.mp3",
  },
  baseUrl: "./circuloSubtleDrum03/"
}).connect(verb);

m.synths.synth04 = new Tone.Sampler({
  urls: {
    E2: "circuloSubKick03-E2.mp3",
  },
  baseUrl: "./circuloSubKick03/"
}).connect(comp);

m.synths.synth05 = new Tone.Sampler({
  urls: {
    A2: "bkThumpKick-A2.mp3",
  },
  baseUrl: "./bkThumpKick/"
}).connect(comp);

m.synths.synth06 = new Tone.Sampler({
  urls: {
    G2: "808Kick-G2.mp3",
  },
  baseUrl: "./808Kick/"
}).connect(comp);

m.synths.synth07 = new Tone.Sampler({
  urls: {
    G2: "808Kick-long-G2.mp3",
  },
  baseUrl: "./808Kick/"
}).connect(comp);

// the players
m.synths.player1 = new Tone.Player({
        url: "./the-stage-phase-1-vox-raw.mp3",
        onload: console.log("m.synths.player1 is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player1b = new Tone.Player({
        url: "./the-stage-phase-1-vox-raw.mp3",
        onload: console.log("m.synths.player1b is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player2 = new Tone.Player({
        url: "./the-stage-phase-2-vox-proc-dark.mp3",
        onload: console.log("m.synths.player2 is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player2b = new Tone.Player({
        url: "./the-stage-phase-2-vox-proc-dark.mp3",
        onload: console.log("m.synths.player2b is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player3 = new Tone.Player({
        url: "./the-stage-phase-3-vox-proc-dark.mp3",
        onload: console.log("m.synths.player3 is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player3b = new Tone.Player({
        url: "./the-stage-phase-3-vox-proc-dark.mp3",
        onload: console.log("m.synths.player3b is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player4 = new Tone.Player({
        url: "./the-stage-phase-4-vox-proc-blurred.mp3",
        onload: console.log("m.synths.player4 is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player4b = new Tone.Player({
        url: "./the-stage-phase-4-vox-proc-blurred.mp3",
        onload: console.log("m.synths.player4b is loaded."),
        loop: true,
        loopStart: 0.0,
        loopEnd: 0.7,
        fadeIn: 0.01,
        fadeOut: 0.01
        }).toDestination();

m.synths.player1.chain(comp, Tone.Destination);
m.synths.player1b.chain(comp, Tone.Destination);
m.synths.player2.chain(comp, Tone.Destination);
m.synths.player2b.chain(comp, Tone.Destination);
m.synths.player3.chain(comp, Tone.Destination);
m.synths.player3b.chain(comp, Tone.Destination);
m.synths.player4.chain(comp, Tone.Destination);
m.synths.player4b.chain(comp, Tone.Destination);





//--------------------------------------------------------------------------
// mixing

m.synths.synth04.volume.value = -6;

m.synths.player1.volume.value = -11;
m.synths.player1b.volume.value = -11;
m.synths.player2.volume.value = -13;
m.synths.player2b.volume.value = -13;
m.synths.player3.volume.value = -11;
m.synths.player3b.volume.value = -11;
m.synths.player4.volume.value = -10.5;
m.synths.player4b.volume.value = -10.5;

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

m.rhythms.rhythm1 = [0].concat(low2HighSort(numArray(0,32).map(x => Math.random()).map(x => x * 32)));
m.rhythms.rhythm2 = [0];
m.rhythms.rhythm3 = [0].concat(low2HighSort(numArray(0,16).map(x => Math.random()).map(x => x * 16)));

//--------------------------------------------------------------------------
// sequences, probably to be merged into the m object above

sequence1 = {triggered: 0};



//--------------------------------------------------------------------------
// initiate storage of vocabulary
// and populate it with initial data


let vocabulary = {};

// BREATHING
vocabulary["breathing"] = {}
vocabulary["breathing"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
    document.querySelector("#stage01").style.opacity=value.args[0];
    document.querySelector(".stage01").style.transform=`scale(${value.args[1]})`;
      console.log('dancer1: breathing ()');
         }, time);

     })

vocabulary["breathing_phase01"] = {};
vocabulary["breathing_phase01"].tone =
    ((time, value) => { m.synths.synth04.triggerAttackRelease(pick(m.motifs.riffB), "8n", time, value.velocity); 
        console.log("dancer103, breathing: " + Tone.Transport.position);
        var element = document.querySelector(".stage103");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 500);
    });

vocabulary["breathing_phase02"] = {};
vocabulary["breathing_phase02"].tone =
    ((time, value) => { m.synths.synth05.triggerAttackRelease(pick(m.motifs.riffB), "8n", time, value.velocity); 
        console.log("dancer103, breathing: " + Tone.Transport.position)
        var element = document.querySelector(".stage103");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 500);
    });

vocabulary["breathing_phase03"] = {};
vocabulary["breathing_phase03"].tone =
    ((time, value) => { m.synths.synth06.triggerAttackRelease(pick(m.motifs.riffB), "8n", time, value.velocity); 
        console.log("dancer103, breathing: " + Tone.Transport.position) 
        var element = document.querySelector(".stage103");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 500);
    });

vocabulary["breathing_phase04"] = {};
vocabulary["breathing_phase04"].tone =
    ((time, value) => { m.synths.synth07.triggerAttackRelease(pick(m.motifs.riffB), "8n", time, value.velocity); 
        console.log("dancer103, breathing: " + Tone.Transport.position) 
        var element = document.querySelector(".stage103");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 500);
    });

// DECENTRING
vocabulary["decentering"] = {}
vocabulary["decentering"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
      x.style.transform = `skew(${value.args[0]})`;  // "skew(" + deg + "deg)";
      console.log('dancer 01: decentering ()');
         }, time);

     })

// VICIOUS CIRCLE
vocabulary["vicious_circle"] = {}
vocabulary["vicious_circle"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
      x.classList.toggle('rotating');
      console.log('dancer 01: vicious_circle ()');
         }, time);

     })

// PUBLIC PRIVATE
vocabulary["public or private"] = {}
vocabulary["public or private"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
    if (y.style.display=="none") {
      (y.style.display="inline") }
    else { (y.style.display="none") };
    y.style.opacity=value.args[0];
    y.style.transform=`scale(${value.args[1]})`;
      console.log('dancer 02: public_or_private ()');
         }, time);

     })

// DISSOLVING
vocabulary["dissolving"] = {}
vocabulary["dissolving"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
        if (y.className == 'triangle') {
            y.className = 'stage02'; 
          } 
        else { y.className = 'triangle'; 
        }
      console.log('dancer 02: dissolving ()');
        }, time);
     })

// TRANSLATING
vocabulary["translating"] = {}
vocabulary["translating"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
        y.classList.toggle('sliding');
      console.log('dancer 02: translating ()');
        }, time);
     })

// MORPHING
vocabulary["morphing"] = {}
vocabulary["morphing"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
        y.classList.toggle('otherthan');
      console.log('dancer 02: morphing ()');
        }, time);
     })

// EXPANDING THE VOID
vocabulary["expanding_the_void"] = {}
vocabulary["expanding_the_void"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
    document.querySelector("#text").style.letterSpacing= value.args[0]; //number + "px";
      document.querySelector("#text").style.transform=`scale(${value.args[1]})`;
      console.log('dancer 03: expanding_the_void ()');
        }, time);
     })

// CIRCUMSCRIBING
vocabulary["circumscribing_phase01"] = {}
vocabulary["circumscribing_phase01"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
              var disDiv = document.querySelector("html");
              if (disDiv.className == 'phase01') {
                disDiv.className = 'nophase01';
              } else {
                disDiv.className = 'phase01';
                }
      console.log('dancer 05: circumscribing_phase01 ()');
        }, time);
     })

vocabulary["circumscribing_phase01"].tone = 
    ((time) => { 
        randomLoopPoints (m.synths.player1, 0.6, 2.5); 
        randomLoopPoints (m.synths.player1b, 0.6, 2.5); 
        console.log("dancer105, circumscribing_phase01; changed the loop at:" + Tone.Transport.position) 
        var element = document.querySelector(".stage105");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 500);
    });


vocabulary["circumscribing_phase02"] = {}
vocabulary["circumscribing_phase02"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
              var disDiv = document.querySelector("html");
              if (disDiv.className == 'phase02') {
                disDiv.className = 'nophase02';
              } else {
                disDiv.className = 'phase02';
                }
      console.log('dancer 05: circumscribing_phase02 ()');
        }, time);
     })

vocabulary["circumscribing_phase02"].tone = 
    ((time) => { 
        randomLoopPoints (m.synths.player2, 0.5, 2.5); 
        randomLoopPoints (m.synths.player2b, 0.5, 2.5); 
        console.log("dancer106, circumscribing_phase02; changed the loop at:" + Tone.Transport.position) 
        var element = document.querySelector(".stage106");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 1000);
    });

vocabulary["circumscribing_phase03"] = {}
vocabulary["circumscribing_phase03"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
              var disDiv = document.querySelector("html");
              if (disDiv.className == 'phase03') {
                disDiv.className = 'nophase03';
              } else {
                disDiv.className = 'phase03';
                }
      console.log('dancer 05: circumscribing_phase03 ()');
        }, time);
     })

vocabulary["circumscribing_phase03"].tone = 
    ((time) => { 
        randomLoopPoints (m.synths.player3, 0.8, 3.0); 
        randomLoopPoints (m.synths.player3b, 0.4, 2.7); 
        console.log("dancer107, circumscribing_phase03; changed the loop at:" + Tone.Transport.position) 
        var element = document.querySelector(".stage107");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 1000);
    });

vocabulary["circumscribing_phase04"] = {}
vocabulary["circumscribing_phase04"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
              var disDiv = document.querySelector("html");
              if (disDiv.className == 'phase04') {
                disDiv.className = 'nophase04';
              } else {
                disDiv.className = 'phase04';
                }
      console.log('dancer 05: circumscribing_phase04 ()');
        }, time);
     })

vocabulary["circumscribing_phase04"].tone = 
    ((time) => { 
        randomLoopPoints (m.synths.player4, 0.9, 3.9); 
        randomLoopPoints (m.synths.player4b, 0.3, 3.0); 
        console.log("dancer108, circumscribing_phase03; changed the loop at:" + Tone.Transport.position) 
        var element = document.querySelector(".stage108");
        element.classList.add("blink");
        setTimeout(function(){element.classList.remove("blink"); }, 1000);
    });

vocabulary["showing"] = {}
vocabulary["showing"].DOM =
     ((time,value) => { Tone.Draw.schedule(() => {
    document.querySelector("#console").style.opacity=value.args[0];
    document.querySelector("#console").style.transform=`scale(${value.args[1]})`;
      console.log('dancer 06: showing ()');
        }, time);
     })


vocabulary["sequencing"] = {}
vocabulary["sequencing"].DOM =
      ((time,value) => { Tone.Draw.schedule(() => { 
           console.log("dancer109 (sequencer dancer using vocab 'squence') ran at " + Tone.Transport.position);
    var element = document.querySelector(".stage109");
    element.classList.add("blink");
    setTimeout(function(){element.classList.remove("blink"); }, 3000);
    trigger = (sequence1.triggered)%4;
    sequence1.triggered = trigger + 1
    if (trigger == 0) {

    // PHASE 1
    m.synths.player3.stop();
    m.synths.player3b.stop();

    p.dancers.dancer103.part.callback = vocabulary["breathing_phase01"].tone;

    m.synths.player4.stop(inNMeasures(3));
    m.synths.player4b.stop(inNMeasures(3));
    m.synths.player1.start(inNMeasures(3));
    m.synths.player1b.start(inNMeasures(3));
    p.dancers.dancer105.part.start(inNMeasures(3));
    p.dancers.dancer108.part.stop(inNMeasures(3));
    p.dancers.dancer103.part.loopEnd = "1m";
    //p.dancers.dancer105.part.loopEnd = "1m";
    p.dancers.dancer103.part.probability = 0.95
    p.dancers.dancer105.part.probability = 0.5

    p.dancers.dancer1.part.start(inNMeasures(2))
    p.dancers.dancer2.part.start(inNMeasures(2))
    p.dancers.dancer3.part.start(inNMeasures(2))
    p.dancers.dancer4.part.start(inNMeasures(2))
    p.dancers.dancer5.part.start(inNMeasures(2))
    // DANCER 01
    changeRhythmAndArgs("dancer1",[0,5,14,17,31], [["0.5", "3"],["0.9", "1"],["0.05", "1"],["1", "0.5"],["0.25", "1"]])
    changeVocab("dancer1", vocabulary["breathing"].DOM)
    // DANCER 02
    changeRhythmAndArgs("dancer2",[0,1,8,11], [["0.5", "2"],["0.7", "1"]])
    changeVocab("dancer2", vocabulary["public or private"].DOM)
    // DANCER 03
    changeRhythmAndArgs("dancer3",[0,1,8,11,14,15], [["200px", "10"],["5px", "1"], ["5px", "5"],])
    changeVocab("dancer3", vocabulary["expanding_the_void"].DOM)
    // DANCER 04
    changeRhythmAndArgs("dancer4",[0,5,14,17,31], [["0.75", "1"],["0.35", "1"],["1", "3"],["0.5", "0.5"]])
    changeVocab("dancer4", vocabulary["showing"].DOM)
    // DANCER 05
    changeRhythmAndArgs("dancer5",[0], [[]])
    changeVocab("dancer5", vocabulary["circumscribing_phase01"].DOM)

    console.log("--- PHASE 1 ---")
   }
    else if (trigger == 1) {

    // PHASE 2
    m.synths.player4.stop();
    m.synths.player4b.stop();

    p.dancers.dancer103.part.callback = vocabulary["breathing_phase02"].tone;

    m.synths.player1.stop(inNMeasures(3));
    m.synths.player1b.stop(inNMeasures(3));
    m.synths.player2.start(inNMeasures(3));
    m.synths.player2b.start(inNMeasures(3));
    p.dancers.dancer106.part.start(inNMeasures(3));
    p.dancers.dancer105.part.stop(inNMeasures(3));
    p.dancers.dancer103.part.loopEnd = "2m";
    //p.dancers.dancer106.part.loopEnd = "2m";
    p.dancers.dancer103.part.probability = 0.9
    p.dancers.dancer106.part.probability = 0.3

    // DANCER 01
    changeRhythmAndArgs("dancer1",[0,5,14,17,20,23,29,31], [["50deg"],["0deg"],["-100deg"],["600deg"]])
    changeVocab("dancer1", vocabulary["decentering"].DOM)
    // DANCER 02
    changeRhythmAndArgs("dancer2",[0,1,8,11,13,15], [[]])
    changeVocab("dancer2", vocabulary["dissolving"].DOM)
    // DANCER 03
    // DANCER 04
    changeRhythmAndArgs("dancer4",[0,1,8,20,22], [["0.3", "2"],["1", "1"]])
    changeVocab("dancer4", vocabulary["showing"].DOM)
    // DANCER 05
    changeRhythmAndArgs("dancer5",[0], [[]])
    changeVocab("dancer5", vocabulary["circumscribing_phase02"].DOM)

    console.log("--- PHASE 2 ---")
    }
    else if (trigger == 2) {

    // PHASE 3
    m.synths.player1.stop();
    m.synths.player1b.stop();

    p.dancers.dancer103.part.callback = vocabulary["breathing_phase03"].tone;

    m.synths.player2.stop(inNMeasures(3));
    m.synths.player2b.stop(inNMeasures(3));
    m.synths.player3.start(inNMeasures(3));
    m.synths.player3b.start(inNMeasures(3));
    p.dancers.dancer107.part.start(inNMeasures(3));
    p.dancers.dancer106.part.stop(inNMeasures(3));
    p.dancers.dancer103.part.loopEnd = "8n";
    //p.dancers.dancer107.part.loopEnd = "4m";
    p.dancers.dancer103.part.probability = 0.5
    p.dancers.dancer107.part.probability = 0.8
    console.log("--- PHASE 3 ---")

    // DANCER 01
    changeRhythmAndArgs("dancer1",[0,14,31], [["0.5", "3"],["0.1", "2"],["0.9", "1"]])
    changeVocab("dancer1", vocabulary["breathing"].DOM)
    // DANCER 02
    changeRhythmAndArgs("dancer2",[0,1,8,10,12,15], [[]])
    changeVocab("dancer2", vocabulary["translating"].DOM)
    // DANCER 03
    changeRhythmAndArgs("dancer3",[0,8,10,12], [["200px", "10"],["5px", "1"], ["5px", "5"],])
    changeVocab("dancer3", vocabulary["expanding_the_void"].DOM)
    // DANCER 04
    changeRhythmAndArgs("dancer4",[0,1,8,20,22], [["0.1", "2"],["0.6", "0.5"]])
    changeVocab("dancer4", vocabulary["showing"].DOM)
    // PHASE 05
    changeRhythmAndArgs("dancer5",[0], [[]])
    changeVocab("dancer5", vocabulary["circumscribing_phase03"].DOM)

    }
    else if (trigger == 3) {

    // PHASE 4
    m.synths.player2.stop();
    m.synths.player2b.stop();
    
    p.dancers.dancer103.part.callback = vocabulary["breathing_phase04"].tone;

    m.synths.player3.stop(inNMeasures(3));
    m.synths.player3b.stop(inNMeasures(3));
    m.synths.player4.start(inNMeasures(3));
    m.synths.player4b.start(inNMeasures(3));
    p.dancers.dancer108.part.start(inNMeasures(3));
    p.dancers.dancer107.part.stop(inNMeasures(3));
    p.dancers.dancer103.part.loopEnd = "2m";
    p.dancers.dancer103.part.probability = 0.3
    p.dancers.dancer108.part.probability = 0.3

    // DANCER 01
    changeRhythmAndArgs("dancer1",[0], [[]])
    changeVocab("dancer1", vocabulary["vicious_circle"].DOM)
    // DANCER 02
    changeRhythmAndArgs("dancer2",[0,1,8,11], [[]])
    changeVocab("dancer2", vocabulary["morphing"].DOM)
    // DANCER 03
    // DANCER 04
    changeRhythmAndArgs("dancer4",[0,1,8,20,22], [["0.3", "5"],["1", "0.5"]])
    changeVocab("dancer4", vocabulary["showing"].DOM)
    // PHASE 05
    changeRhythmAndArgs("dancer5",[0], [[]])
    changeVocab("dancer5", vocabulary["circumscribing_phase04"].DOM)

    console.log("--- PHASE 4 ---")
    }
    else {
    console.log("this should never run")
    }
          }, time);
      })

// use this to see all of the registered vocabulary

Object.keys(vocabulary)




//-------------------------------------------------------------------------- 
// CODE FOR IFRAME AND TEXT ARRAY
//--------------------------------------------------------------------------

    // — CIRCULATING —
    // text array

    var delay="10"; 
    var count='0';
    var Texts=new Array();
    Texts[0]="I dreamed that I was balancing on the edge of a pit.";
    Texts[1]="In my dancing I was balancing from orbit to orbit.";
    Texts[2]="Or, rather, in decentering, dissolving the - us.";
    Texts[3]="Expanding the void.";
    Texts[4]="I dreamed that I was balancing in the edge of..";
    Texts[5]="What if it takes sensing the abyss? The edges of the limits of...";
    Texts[6]="...the limits of inclusion and exclusion,..";
    Texts[7]="before the binary of inside — outside, inclusion — exclusion.";
    Texts[8]="The private circle; the public sphere.";
    Texts[9]="Spherical. Cyclical. Circulation of affects.";
    Texts[10]="Vicious circle. Cut the circuit.";
    Texts[11]="When I awoke it seemed that I could have simply lowered my centre of balance.";
    Texts[12]="...my centre of — balance.";
    Texts[13]="I dreamed that I was balancing on the edge of a...";
    Texts[14]="Sonhei que estava a equilibrar-me no limite de um poço.";
    Texts[15]="Na minha dança eu estava a balançar de órbita em órbita.";
    Texts[16]="E se for preciso sentir o abismo, as margens dos limites...";
    Texts[17]="os limites da inclusão - exclusão - antes do binário de dentro - fora, inclusão - exclusão.";
    Texts[18]="O círculo privado e a esfera pública.";
    Texts[19]="Esférica. Cíclico. Circulação de afectos.";
    Texts[20]="Círculo vicioso. Cortar o circuito.";
    Texts[21]="Quando acordei, parecia que podia simplesmente ter baixado o meu centro de equilíbrio";
    Texts[22]="Circular, correr de boca em boca.";
    Texts[23]="Correr de boca em boca";
    Texts[24]="Que volta ao ponto de que partiu."

    function circulating(){
            document.querySelector('#text').innerHTML = Texts[count]; 
            count++;
            if(count==Texts.length){count='0';}
            setTimeout("circulating()",12000); // should take 4min;

            console.log('dancer 03: circulating ()   \n  \n ');
    } 

    // — ORBITING —
    // array iframe

    var dance = new Array(
        // dream
        "https://en.wikipedia.org/wiki/Portal:Current_events",
        // balance
        "https://en.wiktionary.org/wiki/balance#English",
        // dance
        "https://en.wikinews.org/w/index.php?title=Special:Search&search=dance&fulltext=1&profile=default&ns0=1&ns14=1",
        // dissolving 
        "https://en.wikipedia.org/wiki/Solvation",
        "https://en.wikipedia.org/wiki/Category:Dissolutions_of_political_entities",
        // decentering
        "https://pageviews.toolforge.org/?project=en.wikipedia.org&platform=all-access&agent=user&redirects=0&range=latest-20&pages=Language",
        // expanding
        "https://en.wikisource.org/wiki/1911_Encyclop%C3%A6dia_Britannica/Geometry", 
        // limits
        "https://en.wikipedia.org/wiki/Wikipedia:Contents/Glossaries",
        // public
        "https://para.toolforge.org/Commons:Special:NewFiles",
        // spherical
        "https://pt.wikipedia.org/wiki/Esfera",
        "https://commons.wikimedia.org/w/index.php?search=sphere&title=Special%3ASearch&go=Go&ns0=1&ns6=1&ns12=1&ns14=1&ns100=1&ns106=1",
        // cyclical
        "https://en.wikipedia.org/w/index.php?title=Spinning_jenny&action=history",  
        "https://pt.wikipedia.org/w/index.php?search=lua+fases&title=Especial%3APesquisar&go=Ir&ns0=1",
        // circulation
      "https://commons.wikimedia.org/w/index.php?search=circulating&title=Special:Search&profile=advanced&fulltext=1&advancedSearch-current=%7B%7D&ns0=1&ns6=1&ns12=1&ns14=1&ns100=1&ns106=1",
      "https://upload.wikimedia.org/wikipedia/commons/7/7a/Equation_of_time.svg",
        // sonho
        "https://en.wikipedia.org/wiki/Portal:Current_events",
        // balanço
        "https://en.wiktionary.org/wiki/Balan%C3%A7a",
        // dissolver
        "https://pt.wikipedia.org/w/index.php?search=dissolver&title=Especial%3APesquisar&go=Ir&ns0=1",
        // des-centrado
        "https://pt.wikivoyage.org/wiki/Wikivoyage:Fontes_de_mapas",
        // expandir
        "https://pt.wikipedia.org/wiki/Wikip%C3%A9dia:Coordena%C3%A7%C3%A3o_rob%C3%B3tica",
        // limites 
        "https://pt.wikivoyage.org/wiki/Lista_de_guias_de_conversa%C3%A7%C3%A3o",
        "https://upload.wikimedia.org/wikipedia/commons/a/a1/AS15-90-12319_Messier_crater.jpg",
        // publico 
        "https://pt.wikisource.org/w/index.php?search=publico&title=Especial%3APesquisar&go=Ir&ns0=1&ns102=1&ns104=1",
        // esferico
        "https://pt.wikisource.org/wiki/Wikisource:Pol%C3%ADticas_e_orienta%C3%A7%C3%B5es_oficiais",
        // ciclico
        "https://commons.wikimedia.org/wiki/Moon#Moon/_Mond/_Luna",
        // circulação
      "https://pt.wikipedia.org/wiki/Circula%C3%A7%C3%A3o", 
      "https://pt.wikipedia.org/wiki/Circular_(desambigua%C3%A7%C3%A3o)",
       "https://commons.wikimedia.org/wiki/Template:Potd/2021-02"
    );

    function from_orbit_to_orbit() {
      danceIndex = 0;
      idInterval = 0;
      noOrbit = setInterval(function() {
        danceIndex = (danceIndex + 1) % dance.length;
        document.getElementById("stage01").src = dance[danceIndex];
      },15000);
      console.log('dancer 01: from_orbit_to_orbit ()   \n  \n ');
    }

    function no_orbit (){
          clearInterval(noOrbit);
          console.log('no_orbit ()  \n  \n ');
    }




//--------------------------------------------------------------------------
// generate this performance

Tone.Transport.bpm.value = 160;

p = new Performance();
p.newdancer('dancer1')
p.newdancer('dancer2')
p.newdancer('dancer3')
p.newdancer('dancer4')
p.newdancer('dancer5')
p.newdancer('dancer103')
p.newdancer('dancer105')
p.newdancer('dancer106')
p.newdancer('dancer107')
p.newdancer('dancer108')
p.newdancer('dancer109')

p.dancerNames()

//--------------------------------------------------------------------------
// ADD DANCERS TO generate this performance
// 4 beats per measure 4* 12 = 48

//--------------------------------------------------------------------------
// Dancer 01 — > stage 01 (iframe)
  p.dancers.dancer1.rhythm = m.rhythms.rhythm1.slice() 
  p.dancers.dancer1.part = new Tone.Part(
      ((time, value) => {console.log("dancer1:" + Tone.Transport.position) })
      , beatsToEvents(p.dancers.dancer1.rhythm)
  )
  p.dancers.dancer1.part.loop = true;
  p.dancers.dancer1.part.loopEnd = "8m";
  p.dancers.dancer1.part.probability = 0.7

    changeRhythmAndArgs("dancer1",[0,5,14,17,20,23,29,31], [["50deg"],["0deg"],["-100deg"],["600deg"]])
    changeVocab("dancer1", vocabulary["decentering"].DOM)

//--------------------------------------------------------------------------
// Dancer 02 — > stage 02 (shape)
  p.dancers.dancer2.rhythm = m.rhythms.rhythm1.slice() 
  p.dancers.dancer2.part = new Tone.Part(
  ((time) => { console.log("dancer2:" + Tone.Transport.position)})
  , beatsToEvents(p.dancers.dancer2.rhythm)
  )
  p.dancers.dancer2.part.loop = true;
  p.dancers.dancer2.part.loopEnd = "8m";

  // phase 01 opacity, scale: add more steps up to 8 ["0.7", "1"]

    changeRhythmAndArgs("dancer2",[0,1,8,11,13,15], [[]])
    changeVocab("dancer2", vocabulary["dissolving"].DOM)

//--------------------------------------------------------------------------
// Dancer 03 — > stage 03 (text)
  p.dancers.dancer3.rhythm = m.rhythms.rhythm1.slice() 
  p.dancers.dancer3.part = new Tone.Part(
      ((time, value) => {console.log("dancer3:" + Tone.Transport.position) })
      , beatsToEvents(p.dancers.dancer3.rhythm)
  )
  p.dancers.dancer3.part.loop = true;
  p.dancers.dancer3.part.loopEnd = "4m"; 

    changeRhythmAndArgs("dancer3",[0,1,8,11,14,15], [["200px", "10"],["5px", "1"], ["5px", "5"],])
    changeVocab("dancer3", vocabulary["expanding_the_void"].DOM)

//--------------------------------------------------------------------------
// Dancer 04 — > webconsole div // 8
  p.dancers.dancer4.rhythm = m.rhythms.rhythm2.slice() 
  p.dancers.dancer4.part = new Tone.Part(
      ((time) => { 
          targetDancer = pick( (Object.keys(p.dancers)).filter(x => x !="dancer4"));
          partToggle(p.dancers[targetDancer].part);
          console.log("dancer4 has toggled " + targetDancer + ":" + Tone.Transport.position) 
      })
      , beatsToEvents(p.dancers.dancer4.rhythm)
  )
  p.dancers.dancer4.part.loop = true;
  p.dancers.dancer4.part.loopEnd = "8m";

    changeRhythmAndArgs("dancer4",[0,5,14,17,31], [["0.75", "1"],["0.35", "1"],["1", "3"],["0.5", "0.5"]])
    changeVocab("dancer4", vocabulary["showing"].DOM)

//--------------------------------------------------------------------------
// Dancer 05 — > sets the phases (changes background) — 60s
  p.dancers.dancer5.rhythm = m.rhythms.rhythm2.slice() 
  p.dancers.dancer5.part = new Tone.Part(
      ((time) => { 
          console.log("dancer05:" + Tone.Transport.position) 
      })
      , beatsToEvents(p.dancers.dancer5.rhythm)
  )
  p.dancers.dancer5.part.loop = true;
  p.dancers.dancer5.part.loopEnd = "32m";

    changeRhythmAndArgs("dancer5",[0], [[]])
    changeVocab("dancer5", vocabulary["circumscribing_phase01"].DOM)

//--------------------------------------------------------------------------
// dancer103

p.dancers.dancer103.rhythm = m.rhythms.rhythm1.slice() 
p.dancers.dancer103.part = new Tone.Part(
    vocabulary["breathing_phase01"].tone
    , beatsToEvents(p.dancers.dancer103.rhythm)
)
p.dancers.dancer103.part.loop = true;
p.dancers.dancer103.part.loopEnd = "2m";

//--------------------------------------------------------------------------
// dancer105

p.dancers.dancer105.rhythm = m.rhythms.rhythm2.slice() 
p.dancers.dancer105.part = new Tone.Part(
    vocabulary["circumscribing_phase01"].tone
    , beatsToEvents(p.dancers.dancer105.rhythm)
)
p.dancers.dancer105.part.loop = true;
p.dancers.dancer105.part.loopEnd = "1m";

//--------------------------------------------------------------------------
// dancer106

p.dancers.dancer106.rhythm = m.rhythms.rhythm2.slice() 
p.dancers.dancer106.part = new Tone.Part(
    vocabulary["circumscribing_phase02"].tone
    , beatsToEvents(p.dancers.dancer106.rhythm)
)
p.dancers.dancer106.part.loop = true;
p.dancers.dancer106.part.loopEnd = "2m";

//--------------------------------------------------------------------------
// dancer107

p.dancers.dancer107.rhythm = m.rhythms.rhythm2.slice() 
p.dancers.dancer107.part = new Tone.Part(
    vocabulary["circumscribing_phase03"].tone
    , beatsToEvents(p.dancers.dancer107.rhythm)
)
p.dancers.dancer107.part.loop = true;
p.dancers.dancer107.part.loopEnd = "4m";

//--------------------------------------------------------------------------
// dancer108

p.dancers.dancer108.rhythm = m.rhythms.rhythm2.slice() 
p.dancers.dancer108.part = new Tone.Part(
    vocabulary["circumscribing_phase04"].tone
    , beatsToEvents(p.dancers.dancer108.rhythm)
)
p.dancers.dancer108.part.loop = true;
p.dancers.dancer108.part.loopEnd = "2m";

//--------------------------------------------------------------------------
// dancer109

p.dancers.dancer109.rhythm = m.rhythms.rhythm2.slice() 
p.dancers.dancer109.part = new Tone.Part(
    vocabulary["sequencing"].DOM
    , beatsToEvents(p.dancers.dancer109.rhythm)
)
p.dancers.dancer109.part.probability = 1;
p.dancers.dancer109.part.loop = true;
p.dancers.dancer109.part.loopEnd = "16m";



//--------------------------------------------------------------------------
// bind the interface

  document.getElementById("b").addEventListener("click", () => {console.log("Tone started");Tone.start()});
  document.getElementById("b").addEventListener("click", () => {console.log("starting dancers and player");Tone.Transport.start()
  p.dancers.dancer109.part.start(inNMeasures(3))
  p.dancers.dancer103.part.start(inNMeasures(3))
});