Tone.js
by fxi
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/0.8.0/Tone.min.js"></script>
<button onclick=ba.toggle()> click to start/stop</button>
CSS
html,body {
height:100%;
width:100%;
}
body {
display:flex;
justify-content:center;
align-items:center;
}
JavaScript
const def = {
melody: [
'',
'A3',
'A#3',
'D4',
'F4',
'',
'A2',
'',
'',
'A3',
'A#3',
'D4',
'E4',
'',
'A#2',
''
],
rythm: ['A#0', 'A1', 'A#0', 'A1']
};
class BackgroundAudio {
constructor(opt) {
const ba = this;
ba._opt = Object.assign({}, def, opt);
ba.playNote = ba.playNote.bind(ba);
}
init() {
const ba = this;
if (ba._init) {
return;
}
ba._init = true;
ba._enabled = false;
Tone.Transport.bpm.value = 67;
ba.synth = ba.createSynth();
ba.seq = new Tone.Sequence(ba.playNote, ba._opt.melody, '1n');
ba.seqRythm = new Tone.Sequence(ba.playNote, ba._opt.rythm, '2n');
}
toggle(){
const ba = this;
if (ba._enabled) {
ba.stop();
}else{
ba.start();
}
}
start() {
const ba = this;
if (ba._enabled) {
return;
}
ba.init();
Tone.Transport.start();
ba.seq.start(0);
ba.seqRythm.start();
ba._enabled = true;
}
stop() {
const ba = this;
if (!ba._enabled) {
return;
}
Tone.Transport.stop();
ba.seq.cancel();
ba.seq.stop(0);
ba.seqRythm.cancel();
ba.seqRythm.stop(0);
ba._enabled = false;
}
playNote(time, note) {
const ba = this;
if (time && note != '') {
ba.synth.triggerAttackRelease(note, '1n');
}
}
createSynth() {
const vol = new Tone.Volume(-15).toMaster();
const reverb = new Tone.Freeverb(0.9).connect(vol);
const delay = new Tone.FeedbackDelay(0.304, 0.5).connect(reverb);
const vibrato = new Tone.Vibrato(5, 0.2).connect(delay);
const polySynth = new Tone.PolySynth(3, Tone.Synth, {
oscillator: {
type: 'sine'
},
envelope: {
attack: 0.1,
decay: 1,
sustain: 0.2,
release: 0.1
}
});
reverb.wet.value = 0.1;
delay.wet.value = 0.2;
return polySynth.connect(vibrato);
}
}
window.ba = new BackgroundAudio();