Tone.js/examples/funkyShape.html

227 lines
5.8 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>VISUALIZING ENVELOPES</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://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.4.3/webcomponents-bundle.js"></script>
<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet"/>
<script src="../build/Tone.js"></script>
<script src="./js/tone-ui.js"></script>
<script src="./js/components.js"></script>
</head>
<body>
<tone-example label="Tone with p5.js">
<div slot="explanation">
Access the envelopes current value to synchronize visuals. This sketch uses <a href="https://p5js.org" target="_blank">p5.js</a> for canvas rendering.
<br><br>
Example by <a href="https://github.com/polyrhythmatic">polyrhythmatic</a>
</div>
<div id="content">
<tone-play-toggle></tone-play-toggle>
</div>
</tone-example>
<script type="text/javascript">
new p5((p5) => {
p5.setup = () => {
// create a canvas width and height of the screen
// document.querySelector('canvas')
p5.createCanvas(300, 300);
// no fill
p5.fill(255);
p5.strokeWeight(1);
p5.rectMode(p5.CENTER);
};
let phase = 0;
p5.draw = () => {
p5.background(255);
p5.stroke(0);
// drawing the kick wave at the bottom
// it is composed of a simple sine wave that
// changes in height with the kick envelope
for (let i = 0; i < p5.width; i++) {
// scaling kickEnvelope value by 200
// since default is 0-1
const kickValue = kickEnvelope.value * 200;
// multiplying this value to scale the sine wave
// depending on x position
const yDot = Math.sin((i / 60) + phase) * kickValue;
p5.point(i, p5.height -150 + yDot);
}
// increasing phase means that the kick wave will
// not be standing and looks more dynamic
phase += 1;
// updating circle and square positions with
// bass envelop visualizer
const bassRadius = p5.height * bassEnvelope.value;
p5.stroke("red");
const bassX = p5.noise(p5.millis() / 1000) * p5.width;
const bassY = p5.noise(phase / 100) * p5.height;
p5.ellipse(bassX, bassY, bassRadius, bassRadius);
// beep envelope viz
const beepX = p5.noise(p5.millis() / 500) * p5.width;
const beepY = p5.noise(phase / 50) * p5.height;
const beepSize = p5.height * bleepEnvelope.value;
p5.stroke("green");
p5.rect(beepX, beepY, beepSize, beepSize);
};
}, document.querySelector("#content"));
// filtering the hi-hats a bit
// to make them sound nicer
const lowPass = new Tone.Filter({
frequency: 14000,
}).toDestination();
// we can make our own hi hats with
// the noise synth and a sharp filter envelope
const openHiHat = new Tone.NoiseSynth({
volume: -10,
envelope: {
attack: 0.01,
decay: 0.3
},
}).connect(lowPass);
const openHiHatPart = new Tone.Part(((time) => {
openHiHat.triggerAttack(time);
}), [{ "8n": 2 }, { "8n": 6 }]).start(0);
const closedHiHat = new Tone.NoiseSynth({
volume: -10,
envelope: {
attack: 0.01,
decay: 0.15
},
}).connect(lowPass);
const closedHatPart = new Tone.Part(((time) => {
closedHiHat.triggerAttack(time);
}), [0, { "16n": 1 }, { "8n": 1 }, { "8n": 3 }, { "8n": 4 }, { "8n": 5 }, { "8n": 7 }, { "8n": 8 }]).start(0);
// BASS
const bassEnvelope = new Tone.AmplitudeEnvelope({
attack: 0.01,
decay: 0.2,
sustain: 0,
}).toDestination();
const bassFilter = new Tone.Filter({
frequency: 600,
Q: 8
});
const bass = new Tone.PulseOscillator("A2", 0.4).chain(bassFilter, bassEnvelope);
bass.start();
const bassPart = new Tone.Part(((time, note) => {
bass.frequency.setValueAtTime(note, time);
bassEnvelope.triggerAttack(time);
}), [["0:0", "A1"],
["0:2", "G1"],
["0:2:2", "C2"],
["0:3:2", "A1"]]).start(0);
// BLEEP
const bleepEnvelope = new Tone.AmplitudeEnvelope({
attack: 0.01,
decay: 0.4,
sustain: 0,
}).toDestination();
const bleep = new Tone.Oscillator("A4").connect(bleepEnvelope);
bleep.start();
const bleepLoop = new Tone.Loop(((time) => {
bleepEnvelope.triggerAttack(time);
}), "2n").start(0);
// KICK
const kickEnvelope = new Tone.AmplitudeEnvelope({
attack: 0.01,
decay: 0.2,
sustain: 0,
}).toDestination();
const kick = new Tone.Oscillator("A2").connect(kickEnvelope).start();
const kickSnapEnv = new Tone.FrequencyEnvelope({
attack: 0.005,
decay: 0.01,
sustain: 0,
baseFrequency: "A2",
octaves: 2.7
}).connect(kick.frequency);
const kickPart = new Tone.Part(((time) => {
kickEnvelope.triggerAttack(time);
kickSnapEnv.triggerAttack(time);
}), ["0", "0:0:3", "0:2:0", "0:3:1"]).start(0);
// TRANSPORT
Tone.Transport.loopStart = 0;
Tone.Transport.loopEnd = "1:0";
Tone.Transport.loop = true;
// bind the interface
document.querySelector("tone-play-toggle").addEventListener("start", e => Tone.Transport.start());
document.querySelector("tone-play-toggle").addEventListener("stop", e => Tone.Transport.stop());
const controls = drawer({
parent: document.body,
open: false,
});
controls.folder({
name: "Hihat"
}).add({
tone: lowPass,
}).add({
name: "Open Hihat",
tone: openHiHat,
}).add({
name: "Closed Hihat",
tone: closedHiHat
});
controls.folder({
name: "Bass"
}).add({
tone: bassFilter,
}).add({
tone: bass,
}).add({
tone: bassEnvelope
});
controls.folder({
name: "Bleep"
}).add({
tone: bleep,
}).add({
tone: bleepEnvelope,
});
controls.folder({
name: "Kick"
}).add({
tone: kick,
}).add({
tone: kickEnvelope,
}).add({
tone: kickSnapEnv,
});
</script>
</body>
</html>