Skip to content

Instantly share code, notes, and snippets.

@koolamusic
Last active December 23, 2021 02:41
Show Gist options
  • Save koolamusic/b6eea69382d5217165e28176f07a2be7 to your computer and use it in GitHub Desktop.
Save koolamusic/b6eea69382d5217165e28176f07a2be7 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Tonejs Demo</title>
</head>
<body>
<!-- Learn music theory https://www.youtube.com/watch?v=rgaTLrZGlk0 -->
<!-- Using Tonejs https://www.devbridge.com/articles/tonejs-coding-music-production-guide/
-->
<script src="https://unpkg.com/tone"></script>
<button id="play-button">Play/Pause</button>
<script>
const AMinorScale = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
const addOctaveNumbers = (scale, octaveNumber) => scale.map(note => {
const firstOctaveNoteIndex = scale.indexOf('C') !== -1 ? scale.indexOf('C') : scale.indexOf('C#')
const noteOctaveNumber = scale.indexOf(note) < firstOctaveNoteIndex ? octaveNumber - 1 : octaveNumber;
return `${note}${noteOctaveNumber}`
});
const constructMajorChord = (scale, octave, rootNote) => {
const scaleWithOctave = addOctaveNumbers(scale, octave);
const getNextChordNote = (note, nextNoteNumber) => {
const nextNoteInScaleIndex = scaleWithOctave.indexOf(note) + nextNoteNumber - 1;
let nextNote;
if (typeof(scaleWithOctave[nextNoteInScaleIndex]) !== 'undefined') {
nextNote = scaleWithOctave[nextNoteInScaleIndex];
} else {
nextNote = scaleWithOctave[nextNoteInScaleIndex - 7];
const updatedOctave = parseInt(nextNote.slice(1)) + 1;
nextNote = `${nextNote.slice(0,1)}${updatedOctave}`;
}
return nextNote;
}
const thirdNote = getNextChordNote(rootNote, 3)
const fifthNote = getNextChordNote(rootNote, 5)
const chord = [rootNote, thirdNote, fifthNote]
return chord
}
const constructChords = (scale, octave) => {
const scaleWithOctave = addOctaveNumbers(scale, octave);
const getNextChordNote = (note, nextNoteNumber) => {
const nextNoteInScaleIndex = scaleWithOctave.indexOf(note) + nextNoteNumber - 1;
let nextNote;
if (typeof(scaleWithOctave[nextNoteInScaleIndex]) !== 'undefined') {
nextNote = scaleWithOctave[nextNoteInScaleIndex];
} else {
nextNote = scaleWithOctave[nextNoteInScaleIndex - 6];
const updatedOctave = parseInt(nextNote.slice(1)) + 1;
nextNote = `${nextNote.slice(0,1)}${updatedOctave}`;
}
return nextNote;
}
const chordArray = scaleWithOctave.map(note => {
let thirdNote = getNextChordNote(note, 3)
let fifthNote = getNextChordNote(note, 5)
const chord = [note, thirdNote, fifthNote]
return chord
})
return chordArray;
}
Tone.Transport.bpm.value = 150
const IChord = constructMajorChord(AMinorScale, 4, 'A3');
const VChord = constructMajorChord(AMinorScale, 4, 'E4');
const VIChord = constructMajorChord(AMinorScale, 3, 'F3');
const IVChord = constructMajorChord(AMinorScale, 3, 'D3');
IChord.push('A2', 'G4')
VChord.push('E2', 'G3')
VIChord.push('F2', 'E4')
IVChord.push('D2', 'C4')
console.log(IChord);
console.log(VChord);
console.log(VIChord);
console.log(IVChord);
const synth = new Tone.PolySynth(Tone.Synth, {
maxPolyphony: 5,
volume: -5,
oscillator : {
type : "sawtooth"
}
}).toDestination();
const mainChords = [
{'time': 0, 'note': IChord, 'duration': '2n.'},
{'time': '0:3', 'note': VChord, 'duration': '4n'},
{'time': '1:0', 'note': VIChord, 'duration': '2n.'},
{'time': '1:3', 'note': VChord, 'duration': '4n'},
{'time': '2:0', 'note': IVChord, 'duration': '2n.'},
{'time': '2:3', 'note': VChord, 'duration': '4n'},
{'time': '3:0', 'note': VIChord, 'duration': '2n'},
{'time': '3:2', 'note': VChord, 'duration': '4n'},
{'time': '3:3', 'note': IVChord, 'duration': '4n'},
{'time': '4:0', 'note': IChord, 'duration': '2n.'},
{'time': '4:3', 'note': VChord, 'duration': '4n'},
{'time': '5:0', 'note': VIChord, 'duration': '2n.'},
{'time': '5:3', 'note': VChord, 'duration': '4n'},
{'time': '6:0', 'note': IVChord, 'duration': '2n.'},
{'time': '6:3', 'note': VChord, 'duration': '4n'},
{'time': '7:0', 'note': VIChord, 'duration': '2n'},
{'time': '7:2', 'note': VChord, 'duration': '4n'},
{'time': '7:3', 'note': IVChord, 'duration': '4n'},
];
const part = new Tone.Part(function(time, note){
synth.triggerAttackRelease(note.note, note.duration, time);
}, mainChords).start(0);
const IChord1 = constructMajorChord(AMinorScale, 5, 'A4');
const VChord1 = constructMajorChord(AMinorScale, 5, 'E5');
const VIChord1= constructMajorChord(AMinorScale, 4, 'F4');
const IVChord1 = constructMajorChord(AMinorScale, 4, 'D4');
IChord.push('A3', 'G5')
VChord.push('E3', 'D5')
VIChord.push('F3', 'E5')
IVChord.push('D3', 'C5')
const mainChordPart = new Tone.PolySynth(Tone.Synth, {
maxPolyphony: 5,
oscillator : {
count: 6,
spread: 80,
type : "fatsawtooth"
}
}).toDestination();
const highOctaveChords = [
{'time': 0, 'note': IChord1, 'duration': '2n.'},
{'time': '0:3', 'note': VChord1, 'duration': '4n'},
{'time': '1:0', 'note': VIChord1, 'duration': '2n.'},
{'time': '1:3', 'note': VChord1, 'duration': '4n'},
{'time': '2:0', 'note': IVChord1, 'duration': '2n.'},
{'time': '2:3', 'note': VChord1, 'duration': '4n'},
{'time': '3:0', 'note': VIChord1, 'duration': '2n'},
{'time': '3:2', 'note': VChord1, 'duration': '4n'},
{'time': '3:3', 'note': IVChord1, 'duration': '4n'},
{'time': '4:0', 'note': IChord1, 'duration': '2n.'},
{'time': '4:3', 'note': VChord1, 'duration': '4n'},
{'time': '5:0', 'note': VIChord1, 'duration': '2n.'},
{'time': '5:3', 'note': VChord1, 'duration': '4n'},
{'time': '6:0', 'note': IVChord1, 'duration': '2n.'},
{'time': '6:3', 'note': VChord1, 'duration': '4n'},
{'time': '7:0', 'note': VIChord1, 'duration': '2n'},
{'time': '7:2', 'note': VChord1, 'duration': '4n'},
{'time': '7:3', 'note': IVChord1, 'duration': '4n'},
];
const highSynth = new Tone.PolySynth(Tone.Synth, {
volume: -16,
maxPolyphony: 5,
count: 6,
spread: 80,
oscillator : {
type : "fatsawtooth"
}
}).toDestination();
const highOctaveChordPart = new Tone.Part(function(time, note){
highSynth.triggerAttackRelease(note.note, note.duration, time, 0.5);
}, highOctaveChords).start(0);
const mainMelody = [
{'time': 0, 'note': 'G4', 'duration': '8n'},
{'time': '0:0:2', 'note': 'F4', 'duration': '8n'},
{'time': '0:1', 'note': 'D4', 'duration': '8n.'},
{'time': '0:2', 'note': 'D4', 'duration': '8n'},
{'time': '0:2:2', 'note': 'F4', 'duration': '8n.'},
{'time': '0:3', 'note': 'G4', 'duration': '8n'},
{'time': '0:3:2', 'note': 'A4', 'duration': '2n'},
{'time': '2:0', 'note': 'A4', 'duration': '8n'},
{'time': '2:0:2', 'note': 'G4', 'duration': '8n'},
{'time': '2:1', 'note': 'F4', 'duration': '8n'},
{'time': '2:2', 'note': 'A4', 'duration': '8n'},
{'time': '2:2:2', 'note': 'G4', 'duration': '8n'},
{'time': '2:3', 'note': 'E4', 'duration': '8n'},
{'time': '2:3:2', 'note': 'F4', 'duration': '2n'},
{'time': '4:0', 'note': 'G4', 'duration': '8n'},
{'time': '4:0:2', 'note': 'F4', 'duration': '8n'},
{'time': '4:1', 'note': 'D4', 'duration': '8n'},
{'time': '4:2', 'note': 'F4', 'duration': '8n'},
{'time': '4:2:2', 'note': 'A4', 'duration': '8n'},
{'time': '4:3', 'note': 'G4', 'duration': '8n'},
{'time': '4:3:2', 'note': 'A4', 'duration': '2n'},
{'time': '5:2:2', 'note': 'G4', 'duration': '8n'},
{'time': '5:3', 'note': 'A4', 'duration': '8n'},
{'time': '5:3:2', 'note': 'B4', 'duration': '8n'},
{'time': '6:0', 'note': 'C5', 'duration': '8n'},
{'time': '6:1', 'note': 'B4', 'duration': '8n'},
{'time': '6:1:2', 'note': 'A4', 'duration': '8n'},
{'time': '6:2', 'note': 'B4', 'duration': '8n'},
{'time': '6:2:2', 'note': 'A4', 'duration': '8n'},
{'time': '6:3', 'note': 'G4', 'duration': '8n'},
{'time': '6:3:2', 'note': 'A4', 'duration': '1n'},
];
const synth2 = new Tone.Synth({
oscillator : {
volume: 5,
count: 3,
spread: 40,
type : "fatsawtooth"
}
}).toDestination();
const mainMelodyPart = new Tone.Part(function(time, note){
synth2.triggerAttackRelease(note.note, note.duration, time);
}, mainMelody).start(0);
const lowPass = new Tone.Filter({
frequency: 8000,
}).toDestination();
const snareDrum = new Tone.NoiseSynth({
noise: {
type: 'white',
playbackRate: 3,
},
envelope: {
attack: 0.001,
decay: 0.20,
sustain: 0.15,
release: 0.03,
},
}).connect(lowPass);
const snares = [
{ time: '0:2' },
{ time: '1:2' },
{ time: '2:2' },
{ time: '3:2' },
{ time: '4:2' },
{ time: '5:2' },
{ time: '6:2' },
{ time: '7:2' },
]
const snarePart = new Tone.Part(function(time){
snareDrum.triggerAttackRelease('4n', time)
}, snares).start(0);
const kickDrum = new Tone.MembraneSynth({
volume: 6
}).toDestination();
const kicks = [
{ time: '0:0' },
{ time: '0:3:2' },
{ time: '1:1' },
{ time: '2:0' },
{ time: '2:1:2' },
{ time: '2:3:2' },
{ time: '3:0:2' },
{ time: '3:1:' },
{ time: '4:0' },
{ time: '4:3:2' },
{ time: '5:1' },
{ time: '6:0' },
{ time: '6:1:2' },
{ time: '6:3:2' },
{ time: '7:0:2' },
{ time: '7:1:' },
];
const kickPart = new Tone.Part(function(time){
kickDrum.triggerAttackRelease('C1', '8n', time)
}, kicks).start(0);
const bassline = [
{'time': 0, 'note': 'A0', 'duration': '2n'},
{'time': '0:3', 'note': 'F0', 'duration': '2n.'},
{'time': '1:3', 'note': 'D0', 'duration': '2n.'},
{'time': '2:3', 'note': 'F0', 'duration': '1:1'},
];
const bass = new Tone.Synth({
oscillator : {
type : "triangle"
}
}).toDestination();
const bassPart = new Tone.Part(function(time, note){
bass.triggerAttackRelease(note.note, note.duration, time);
}, bassline).start(0);
document.getElementById("play-button").addEventListener("click", function() {
if (Tone.Transport.state !== 'started') {
Tone.Transport.start();
} else {
Tone.Transport.stop();
}
});
</script>
</body>
</html>
// Use mediarecorder API to create audio recording from context, supported formats [webm, opus, wav]
/** @see https://tonejs.github.io/docs/14.7.77/Recorder */
let ac = new AudioContext()
let audioStream = ac.createMediaStreamDestination().stream;
let recorder = new Recorder(audioStream)
ac.destination.connect(audioStream);
class Recorder {
constructor(audioStream){
this._streams = audioStream.getAudioTracks()[0])
this._chunks = []
this.recorder = new MediaRecorder(this._streams)
this.recorder.ignoreMutedMedia = false
this.recorder.addEventListener('dataavailable', (e) => this.saveChunks(e))
this.recorder.addEventListener('stop', (e) => this.downloadStream(e))
this._mimeType = 'audio/wav'
}
saveChunks(e) {
e.data.size && this._chunks.push(e.data);
}
start(fileName = 'file'){
this._fileName = fileName
this.recorder.start();
}
stop(){
setTimeout(() => this.recorder.stop(), 1000)
}
downloadStream(e) {
if (this._chunks.length) {
let blob = new Blob(this._chunks)
// download the blob to a file
// you can use this helper: http://danml.com/download.html
download(blob, this._fileName + '.wav', this._mimeType);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment