<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Phasing</title> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <link rel="icon" type="image/png" sizes="174x174" href="./favicon.png"> <script src="https://unpkg.com/@webcomponents/webcomponentsjs@^2/webcomponents-bundle.js"></script> <script src="../build/Tone.js"></script> <script src="./js/tonejs-ui.js"></script> </head> <body> <style type="text/css"> #loop { margin-bottom: 10px; display: flex; background-color: #777; border-radius: 20px; padding: 10px; } tone-loop { flex-grow: 1; width: 40%; height: 150px; } </style> <tone-example> <tone-explanation label="Piano Phase"> By slightly slowing down the playbackRate of the Tone.Sequence in the right channel, the two identical melodies phase against each other in interesting ways. <br><br> Composition by Steve Reich. Inspiration from Alexander Chen. </tone-explanation> <tone-content> <div id="loop"> <tone-loop id="left" color="#7F33ED"></tone-loop> <tone-loop id="right" color="#1EDF3E"></tone-loop> </div> <tone-play-toggle></tone-play-toggle> </tone-content> </tone-example> <script type="text/javascript"> //set the bpm and time signature first Tone.Transport.timeSignature = [6, 4]; Tone.Transport.bpm.value = 180; //L/R channel merging var merge = new Tone.Merge(); //a little reverb var reverb = new Tone.Freeverb({ "roomSize" : 0.2, "wet" : 0.3 }); merge.chain(reverb, Tone.Master); //the synth settings var synthSettings = { "oscillator" : { "detune" : 0, "type" : "custom", "partials" : [2, 1, 2, 2], "phase" : 0, "volume" : 0 }, "envelope" : { "attack" : 0.005, "decay" : 0.3, "sustain" : 0.2, "release" : 1, }, "portamento" : 0.01, "volume" : -20 }; //left and right synthesizers var synthL = new Tone.Synth(synthSettings).connect(merge.left); var synthR = new Tone.Synth(synthSettings).connect(merge.right); //the two Tone.Sequences var partL = new Tone.Sequence(function(time, note){ synthL.triggerAttackRelease(note, "8n", time); }, ["E4", "F#4", "B4", "C#5", "D5", "F#4", "E4", "C#5", "B4", "F#4", "D5", "C#5"], "8n").start(); var partR = new Tone.Sequence(function(time, note){ synthR.triggerAttackRelease(note, "8n", time); }, ["E4", "F#4", "B4", "C#5", "D5", "F#4", "E4", "C#5", "B4", "F#4", "D5", "C#5"], "8n").start("2m"); //set the playback rate of the right part to be slightly slower partR.playbackRate = 0.985; document.querySelector("tone-play-toggle").bind(Tone.Transport); document.querySelector("#left").bind(partL); document.querySelector("#right").bind(partR); /*// GUI // Interface.Button({ key : 32, type : "toggle", text : "Start", activeText : "Stop", start : function(){ Tone.Transport.start("+0.1"); }, end : function(){ Tone.Transport.stop(); } }); //draw two circles var leftCanvas = $("#Left"); var rightCanvas = $("#Right"); var leftContext = leftCanvas.get(0).getContext("2d"); var rightContext = rightCanvas.get(0).getContext("2d"); var canvasWidth = leftCanvas.width() * 2; var canvasHeight = leftCanvas.height() * 2; var radius = Math.min(canvasWidth, canvasHeight); function sizeCanvas(){ canvasWidth = leftCanvas.width() * 2; canvasHeight = leftCanvas.height() * 2; radius = Math.min(canvasWidth, canvasHeight) * 0.8; leftContext.canvas.width = canvasWidth; leftContext.canvas.height = canvasHeight; rightContext.canvas.width = canvasWidth; rightContext.canvas.height = canvasHeight; } $(window).on("resize", sizeCanvas); sizeCanvas(); var twoPi = Math.PI * 2; function loop(){ requestAnimationFrame(loop); leftContext.lineWidth = radius * 0.1; rightContext.lineWidth = radius * 0.1; //draw the left progress leftContext.clearRect(0, 0, canvasWidth, canvasHeight); leftContext.strokeStyle = "#7F33ED"; leftContext.save(); leftContext.translate(canvasWidth / 2, canvasHeight / 2); leftContext.rotate(-Math.PI / 2); leftContext.beginPath(); leftContext.arc(0, 0, radius/2, 0, twoPi * partL.progress, false); leftContext.stroke(); leftContext.restore(); //draw the left progress rightContext.clearRect(0, 0, canvasWidth, canvasHeight); rightContext.strokeStyle = "#1EDF3E"; rightContext.save(); rightContext.translate(canvasWidth / 2, canvasHeight / 2); rightContext.rotate(-Math.PI / 2); rightContext.beginPath(); rightContext.arc(0, 0, radius/2, 0, twoPi * partR.progress, false); rightContext.stroke(); rightContext.restore(); } loop();*/ </script> </body> </html>