Pentatonic Synthesizer
Tempo
120 BPM
Arpeggiator
Mode
Ascending
Descending
Up-Down
Play
Stop
// Define the C major pentatonic scale (two octaves) const pentatonicScale = [ 'C4', 'D4', 'E4', 'G4', 'A4', 'C5', 'D5', 'E5', 'G5', 'A5' ]; // Create a synthesizer with reverb const synth = new Tone.Synth({ oscillator: { type: 'sine' }, envelope: { attack: 0.01, decay: 0.3, sustain: 0.7, release: 1.5 } }).toDestination(); const reverb = new Tone.Reverb({ decay: 3, wet: 0.5 }).connect(synth); // Set up arpeggiator const arpeggiator = new Tone.Arpeggiator({ interval: '8n', type: 'up', reverse: false }); // Connect arpeggiator to synth arpeggiator.connect(synth); // Store references to pads and their states const pads = {}; const padStates = {}; let isPlaying = false; let arpeggiatorPattern = []; let arpeggiatorInterval = '8n'; let arpeggiatorType = 'up'; let arpeggiatorReverse = false; let tempo = 120; // Initialize the pads const padGrid = document.getElementById('pad-grid'); pentatonicScale.forEach((note, index) => { const pad = document.createElement('div'); pad.className = 'pad'; pad.dataset.note = note; pad.dataset.index = index; pad.textContent = note; pad.addEventListener('click', () => { playNote(note); togglePadSelection(index); }); padGrid.appendChild(pad); pads[index] = pad; padStates[index] = { active: false, selected: true }; }); // Function to play a note function playNote(note) { // Ensure audio is initialized if (Tone.context.state !== 'running') { Tone.start(); } synth.triggerAttackRelease(note, '8n'); // Mark the pad as active const pad = pads[Array.from(pads).findIndex(p => p.dataset.note === note)]; if (pad) { pad.classList.add('active'); setTimeout(() => { pad.classList.remove('active'); }, 200); } } // Function to toggle pad selection function togglePadSelection(index) { padStates[index].selected = !padStates[index].selected; const pad = pads[index]; if (pad) { if (padStates[index].selected) { pad.classList.add('selected'); } else { pad.classList.remove('selected'); } } updateArpeggiatorPattern(); } // Update the arpeggiator pattern based on selected pads function updateArpeggiatorPattern() { arpeggiatorPattern = []; for (let i = 0; i < pentatonicScale.length; i++) { if (padStates[i].selected) { arpeggiatorPattern.push(pentatonicScale[i]); } } // Update arpeggiator pattern arpeggiator.notes = arpeggiatorPattern; } // Update the arpeggiator settings function updateArpeggiatorSettings() { arpeggiator.interval = arpeggiatorInterval; arpeggiator.type = arpeggiatorType; arpeggiator.reverse = arpeggiatorReverse; } // Play button const playBtn = document.getElementById('play-btn'); playBtn.addEventListener('click', () => { if (!isPlaying) { // Start the arpeggiator arpeggiator.start(); isPlaying = true; } }); // Stop button const stopBtn = document.getElementById('stop-btn'); stopBtn.addEventListener('click', () => { if (isPlaying) { arpeggiator.stop(); isPlaying = false; } }); // Stop button const stopBtn = document.getElementById('stop-btn'); stopBtn.addEventListener('click', () => { if (isPlaying) { arpeggiator.stop(); isPlaying = false; } }); // Tempo control const tempoSlider = document.getElementById('tempo'); const tempoDisplay = document.getElementById('tempo-display'); tempoSlider.addEventListener('input', () => { tempo = parseInt(tempoSlider.value); tempoDisplay.textContent = `${tempo} BPM`; Tone.Transport.bpm.value = tempo; }); // Arpeggiator mode control const arpeggioMode = document.getElementById('arpeggio-mode'); arpeggioMode.addEventListener('change', () => { const mode = arpeggioMode.value; switch(mode) { case 'ascending': arpeggiatorType = 'up'; arpeggiatorReverse = false; break; case 'descending': arpeggiatorType = 'down'; arpeggiatorReverse = false; break; case 'up-down': arpeggiatorType = 'up'; arpeggiatorReverse = true; break; } updateArpeggiatorSettings(); }); // Arpeggiator toggle const arpeggioToggle = document.getElementById('arpeggio-toggle'); arpeggioToggle.addEventListener('change', () => { if (arpeggioToggle.checked) { arpeggiator.start(); isPlaying = true; } else { arpeggiator.stop(); isPlaying = false; } }); // Initialize tempo Tone.Transport.bpm.value = tempo; // Update the arpeggiator pattern initially updateArpeggiatorPattern();