I Used ChatGPT o1-preview to Create a Web Version of the TB-303 and TR-808 (No Coding!)

  • Home
  • I Used ChatGPT o1-preview to Create a Web Version of the TB-303 and TR-808 (No Coding!)
I Used ChatGPT o1-preview to Create a Web Version of the TB-303 and TR-808 (No Coding!)

I showcase the final result of using ChatGPT o1-preview to build web versions of the legendary TB-303 bass synthesizer and TR-808 drum machine—all without writing a single line of code! If you’re curious about how AI can help bring classic music gear to life online, check out what I was able to create with just ChatGPT. No tutorial—just the outcome of an AI-powered project. I had to ask Chatgpt for corrections 2-4 times and some in addition to wishes.

ChatGPT Prompt TB-303

make me a complete HTML/JS script with tone.js for a Roland  tb 303: Here are the features and description: '''The Roland TB-303 (Transistor Bass) is an iconic analog synthesizer and sequencer developed by Roland in the early 1980s. Although originally intended as an accompaniment instrument for guitarists, the TB-303 achieved worldwide fame in the electronic music scene, particularly through its defining role in acid house. Here are the main features and functions of the TB-303:

1. Analog Synthesizer
Monophonic: the TB-303 is a monophonic synthesizer, which means it can only play one note at a time.
Oscillator: It has a single oscillator that can produce two waveforms:
Sawtooth: more aggressive and full-sounding.
Square wave: brighter, more hollow sound.
Transistor-based technology: The TB-303 uses analog circuits, which results in a warm and organic sound.
2. Low-pass filter
24dB/octave low-pass filter: a filter that attenuates higher frequencies and passes low frequencies. This gives the TB-303 its characteristic “bubbling” sound.
Resonance: The filter has adjustable resonance, which when amplified, produces the typical “squelchy” acid sound. This is a central component of the TB-303's distinctive sound.
3. Envelope
AD envelope: The TB-303 has an attack-decay envelope that controls how the sound develops over time.
Decay: The duration of the decay after the note can be adjusted. This has a strong influence on how “sharp” or “soft” the sound is.
4. Portamento (Glide)
The TB-303 offers the possibility of portamento, which means it can create smooth glides between two notes. This is one of the main functions that gives the sound its characteristic “flowing” or “sliding” character, especially in acid house tracks.
5. Accent
Accent is another important function that allows you to emphasize certain notes in a pattern by increasing their volume and filter intensity. This gives the pattern a rhythmic and dynamic character and makes the sound more lively.
6. Sequencer
Pattern-based: The TB-303 uses a 16-step sequencer that allows you to program up to 16 notes per pattern. These notes can be entered in different pitches and with different durations (note or rest).
Pitch Control: The pitch of each note can be set individually.
Up/down transpose: Patterns can be transposed (shifted in pitch) in real time, resulting in dynamic sound changes.
Pattern and track storage: The TB-303 can store multiple patterns and integrate them into a series of tracks to create complete songs.
7. Controls and Indicators
Tuning: This allows you to adjust the overall pitch of the oscillator.
Cutoff: This sets the frequency at which the low-pass filter takes effect. This determines which high-frequency signals are cut by the filter.
Resonance: Boosts the frequencies around the cutoff point, creating the typical “whistling” or “squelchy” sound.
Envelope Mod: Controls how much the envelope affects the filter.
Decay: Adjusts the duration of the envelope after a key is released.
Accent: Determines how much accentuation is applied to a note.
8. Sound Character
Acid sound: The distinctive sound of the TB-303, created by the combination of resonance, cutoff filter and glide function, is what makes the characteristic “acid sound”. It is sharp, resonant and has a metallic, bubbling character that is often modulated to create complex and dynamic soundscapes.
Basslines: Although the TB-303 was originally intended for basslines, it was soon used for lead and effect sounds in electronic music because it is very flexible due to the filter and resonance.
9. Limitations (that led to popularity)
Ease of programming: The TB-303's user interface is relatively simple and has fewer functions compared to modern synthesizers, but this ultimately helped to encourage artists to use the machine creatively and innovatively.
Idiosyncratic sound: Although originally intended as an accompaniment to bass lines, it didn't really sound like a real bass, which made it less popular in commercial music, but later became the main instrument in the emerging electronic music scene.
10. Use and influence
Acid House: The TB-303 is inextricably linked with the acid house of the late 1980s, in particular through artists such as Phuture with their track “Acid Tracks”, which is considered to be one of the first acid house tracks.
Widespread use in electronic music: It was later used by numerous artists and in many genres, including techno, trance, breakbeat and even pop music.
Summary of the main features:
Monophonic analog synthesizer.
Sawtooth and square waves as waveforms.
Low-pass filter with resonance.
16-step sequencer.
Glide and accent for dynamic pattern design.
AD envelope for tonal flexibility.
Compact, simple operation that led to creative sound possibilities.
Characteristic acid sound, especially in electronic music.”

ChatGPT Prompt TR-808

Can you also implement a Roland TR-808 drum machine for me below the TB-303 so that I can use it at the same time as the TB-303. Here are the functions:

1. analog sound generation
Analog circuits create the typical drum sounds, resulting in a warm, distinctive sound.
Unlike digital drum machines, the TR-808 offers an organic, lively sound.
2. Iconic sounds
Kick drum: deep and booming, particularly long-lasting and characteristic of hip-hop and electronic music.
Snare drum: A hollow, crisp and metallic snare sound.
Hi-hats: Crisp and cutting, particularly characterized by the hissing sound of the hi-hat.
Clap: A handclap-like sound, very popular in electronic genres.
Cowbell: A hollow, metallic sound that stands out in many tracks and is unique to the 808.
Toms: low and high toms that can be tonally adjusted.
Cymbal and Ride: Simple, metallic sounds that sound very artificial but often work well in the mix.
3. step sequencer
16-step sequencer: Enables the programming of rhythms in 16 steps per bar.
Multiple patterns: Different patterns can be programmed and linked to create complex rhythms.
A/B pattern option: Enables you to switch between two pattern versions for even more variation.
4. Customized drum parameters
Tone-Pitch: Many sounds, such as the kick and toms, have adjustable pitches.
Decay: The length of the bass drum and hi-hats can be adjusted to create short or long sounds.
Volume: Each drum instrument has its own volume control to adjust it individually in the mix.
5. Accent function
Accent: Accents can be set to emphasize certain steps in a pattern and add more dynamics to the rhythms. This makes some beats sound stronger than others.
 
 
8. polyphony
The TR-808 is polyphonic, meaning it can play several drum sounds simultaneously.
9. Built-in effects and modulation
No built-in effects, but due to the analog circuitry, every sound has a certain peculiarity that is often considered an effect (especially the resonance of the kick and the noise of the hi-hats).
 
11. Sounds available
The TR-808 offers a total of 16 drum sounds, including:

Bass drum (kick)
Snare drum
low/mid/high toms
Closed/open hi-hats
Clap
Rimshot
Cowbell
Cymbal
Maracas
Ride

Try it out

Roland TB-303 und TR-808 Nachbildung mit Tone.js

Roland TB-303

16-Step-Sequencer

TR-808 Drum Machine

Drum Sequencer

Here is the code:

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Roland TB-303 und TR-808 Nachbildung mit Tone.js</title>
    <style>
        /* Bestehendes CSS */
        body {
            font-family: Arial, sans-serif;
            background-color: #222;
            color: #fff;
            text-align: center;
            padding: 20px;
        }
        .control-panel, .drum-control-panel {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
        }
        .control, .drum-control {
            margin: 10px;
            text-align: left;
        }
        label {
            display: block;
            margin-bottom: 5px;
        }
        input[type=range] {
            width: 150px;
        }
        .sequencer {
            margin-top: 20px;
        }
        .steps-container, .drum-steps-container {
            display: flex;
            justify-content: center;
            flex-wrap: wrap;
        }
        .step, .drum-step {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin: 5px;
            width: 40px;
            background-color: #333;
            padding: 5px;
            border-radius: 5px;
            position: relative;
        }
        .step.active, .drum-step.active {
            background-color: #444;
        }
        .step.current, .drum-step.current {
            border: 2px solid #fff;
        }
        .note-slider, .drum-toggle {
            width: 30px;
            transform: rotate(-90deg);
            margin-bottom: 10px;
        }
        .note-value, .drum-label {
            margin-top: 5px;
            font-size: 12px;
        }
        .step label, .drum-step label {
            font-size: 12px;
            margin-top: 5px;
        }
        #playButton, #drumPlayButton {
            padding: 10px 20px;
            font-size: 16px;
            background-color: #444;
            color: #fff;
            border: none;
            cursor: pointer;
            margin-top: 20px;
        }
        #playButton:hover, #drumPlayButton:hover {
            background-color: #555;
        }
        .section-title {
            margin-top: 40px;
        }
        .drum-row {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-bottom: 20px;
            width: 100%;
        }
        .drum-row .drum-label {
            font-weight: bold;
            margin-bottom: 5px;
        }
        .drum-steps {
            display: flex;
            justify-content: center;
        }
        .drum-controls {
            display: flex;
            justify-content: center;
            margin-bottom: 10px;
        }
        .drum-control-group {
            margin: 0 5px;
            text-align: center;
        }
        .drum-control-group label {
            margin-bottom: 0;
            font-size: 12px;
        }
        .drum-control-group input[type=range] {
            width: 60px;
        }
        .stepsslider{
            margin: 70px 0px;
        }
    </style>
</head>
<body>

     

    <!-- TB-303 Synthesizer -->
    <h1>Roland TB-303</h1>

    <div class="control-panel">
        <div class="control">
            <label>Wellenform</label>
            <select id="waveform">
                <option value="sawtooth">Sägezahn (Sawtooth)</option>
                <option value="square">Rechteck (Square)</option>
            </select>
        </div>
        <div class="control">
            <label>Cutoff-Frequenz: <span id="cutoffValue">1000</span> Hz</label>
            <input type="range" id="cutoff" min="100" max="5000" value="1000">
        </div>
        <div class="control">
            <label>Resonanz: <span id="resonanceValue">1</span></label>
            <input type="range" id="resonance" min="0.1" max="20" step="0.1" value="1">
        </div>
        <div class="control">
            <label>Envelope Modulation: <span id="envModValue">0.5</span></label>
            <input type="range" id="envMod" min="0" max="1" step="0.01" value="0.5">
        </div>
        <div class="control">
            <label>Decay: <span id="decayValue">0.5</span> s</label>
            <input type="range" id="decay" min="0.1" max="2" step="0.1" value="0.5">
        </div>
        <div class="control">
            <label>Glide-Zeit: <span id="glideValue">0.1</span> s</label>
            <input type="range" id="glide" min="0" max="1" step="0.01" value="0.1">
        </div>
        <div class="control">
            <label>BPM: <span id="bpmValue">120</span></label>
            <input type="range" id="bpm" min="60" max="180" value="120">
        </div>
    </div>

    <div class="sequencer">
        <h2>16-Step-Sequencer</h2>
        <div id="steps" class="steps-container">
            <!-- Sequencer Steps werden hier eingefügt -->
        </div>
    </div>

    <button id="playButton">Play</button>

    <!-- TR-808 Drum Machine -->
    
    <h1 class="section-title">TR-808 Drum Machine</h1>

    <div class="drum-control-panel">
        <div class="drum-control">
            <label>BPM: <span id="drumBpmValue">120</span></label>
            <input type="range" id="drumBpm" min="60" max="180" value="120">
        </div>
    </div>

    <div class="sequencer">
        <h3>Drum Sequencer</h3>
        <div id="drumSteps" class="drum-steps-container">
            <!-- Drum Sequencer Steps werden hier eingefügt -->
        </div>
    </div>

    <button id="drumPlayButton">Play TR-808</button>

    <!-- Tone.js Bibliothek einbinden -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.39/Tone.min.js"></script>

    <script>
    document.addEventListener('DOMContentLoaded', function() {
        // Synthesizer-Setup
        const synth = new Tone.MonoSynth({
            oscillator: {
                type: 'sawtooth'
            },
            filter: {
                Q: 1,
                type: 'lowpass',
                rolloff: -24
            },
            envelope: {
                attack: 0.01,
                decay: 0.5,
                sustain: 0,
                release: 0.01
            },
            filterEnvelope: {
                attack: 0.01,
                decay: 0.5,
                sustain: 0,
                release: 0.01,
                baseFrequency: 200,
                octaves: 7,
                exponent: 2
            },
            portamento: 0.1
        }).toDestination();

        // Sequencer Steps erstellen
        const stepsContainer = document.getElementById('steps');
        const steps = [];
        for(let i = 0; i < 16; i++) {
            const stepDiv = document.createElement('div');
            stepDiv.className = 'step';
            stepDiv.innerHTML = `
                <label><input type="checkbox" class="active-step" checked> On</label>
                <input type="range" class="note-slider stepsslider" min="36" max="84" value="60" step="1">
                <div class="note-value">60</div>
                <label><input type="checkbox" class="accent" title="Accent"> Accent</label>
                <label><input type="checkbox" class="slide" title="Slide"> Slide</label>
            `;
            stepsContainer.appendChild(stepDiv);
            const step = {
                stepDiv: stepDiv,
                activeInput: stepDiv.querySelector('.active-step'),
                noteSlider: stepDiv.querySelector('.note-slider'),
                noteValueDisplay: stepDiv.querySelector('.note-value'),
                accentInput: stepDiv.querySelector('.accent'),
                slideInput: stepDiv.querySelector('.slide')
            };

            // Event Listener hinzufügen
            step.activeInput.addEventListener('change', function() {
                if (step.activeInput.checked) {
                    step.stepDiv.classList.add('active');
                } else {
                    step.stepDiv.classList.remove('active');
                }
                updateSequenceEvents();
            });
            step.noteSlider.addEventListener('input', function() {
                step.noteValueDisplay.textContent = step.noteSlider.value;
                updateSequenceEvents();
            });
            step.accentInput.addEventListener('change', updateSequenceEvents);
            step.slideInput.addEventListener('change', updateSequenceEvents);

            // Standardmäßig aktiv
            step.stepDiv.classList.add('active');

            steps.push(step);
        }

        // Kontrollelemente
        const waveformControl = document.getElementById('waveform');
        const cutoffControl = document.getElementById('cutoff');
        const cutoffValue = document.getElementById('cutoffValue');
        const resonanceControl = document.getElementById('resonance');
        const resonanceValue = document.getElementById('resonanceValue');
        const envModControl = document.getElementById('envMod');
        const envModValue = document.getElementById('envModValue');
        const decayControl = document.getElementById('decay');
        const decayValue = document.getElementById('decayValue');
        const glideControl = document.getElementById('glide');
        const glideValue = document.getElementById('glideValue');
        const bpmControl = document.getElementById('bpm');
        const bpmValue = document.getElementById('bpmValue');

        waveformControl.addEventListener('change', function() {
            synth.oscillator.type = waveformControl.value;
        });

        cutoffControl.addEventListener('input', function() {
            cutoffValue.textContent = cutoffControl.value;
            synth.filter.frequency.value = cutoffControl.value;
        });

        resonanceControl.addEventListener('input', function() {
            resonanceValue.textContent = resonanceControl.value;
            synth.filter.Q.value = resonanceControl.value;
        });

        envModControl.addEventListener('input', function() {
            envModValue.textContent = envModControl.value;
            synth.filterEnvelope.amount = envModControl.value;
        });

        decayControl.addEventListener('input', function() {
            decayValue.textContent = decayControl.value;
            synth.envelope.decay = decayControl.value;
            synth.filterEnvelope.decay = decayControl.value;
        });

        glideControl.addEventListener('input', function() {
            glideValue.textContent = glideControl.value;
            // synth.portamento = glideControl.value; // Wird in der Sequenz behandelt
        });

        bpmControl.addEventListener('input', function() {
            bpmValue.textContent = bpmControl.value;
            Tone.Transport.bpm.value = bpmControl.value;
        });

        let isPlaying = false;

        // Funktion zum Aktualisieren der Sequenz
        function updateSequenceEvents() {
            const events = steps.map(step => {
                if (!step.activeInput.checked) {
                    return null;
                }
                const noteNumber = parseInt(step.noteSlider.value);
                if (isNaN(noteNumber)) {
                    return null;
                }
                const note = Tone.Frequency(noteNumber, "midi").toNote();
                const accent = step.accentInput.checked ? 1 : 0.5;
                const slide = step.slideInput.checked;
                return { note: note, duration: '16n', velocity: accent, slide: slide };
            });

            sequence.events = events;
        }

        // Sequencer-Setup mit Tone.Sequence
        const sequence = new Tone.Sequence(function(time, note){
            const index = sequence.progress * sequence.length;

            // Alle Schritte zurücksetzen
            steps.forEach(step => {
                step.stepDiv.classList.remove('current');
            });

            // Aktuellen Schritt hervorheben
            const currentStep = steps[Math.floor(index) % steps.length];
            currentStep.stepDiv.classList.add('current');

            if (note) {
                // Glide/Slide behandeln
                if (note.slide) {
                    synth.portamento = parseFloat(glideControl.value);
                } else {
                    synth.portamento = 0;
                }
                synth.triggerAttackRelease(note.note, note.duration, time, note.velocity);
            }
        }, new Array(16).fill(null), '16n');

        document.getElementById('playButton').addEventListener('click', async function() {
            if (!isPlaying) {
                await Tone.start();

                // Sequenz initialisieren
                updateSequenceEvents();

                // Transport konfigurieren
                Tone.Transport.bpm.value = bpmControl.value;
                Tone.Transport.start();

                sequence.start(0);

                isPlaying = true;
                this.textContent = 'Stop';
            } else {
                Tone.Transport.stop();
                sequence.stop();

                // Alle aktuellen Schritte zurücksetzen
                steps.forEach(step => {
                    step.stepDiv.classList.remove('current');
                });

                isPlaying = false;
                this.textContent = 'Play';
            }
        });
    });
                 // ---------------- TR-808 Drum Machine ----------------

        // Drum-Instrumente mit Standardparametern
        const drumKit = {
            kick: new Tone.MembraneSynth().toDestination(),
            snare: {
                synth: new Tone.NoiseSynth(),
                filter: new Tone.Filter(1000, 'highpass').toDestination()
            },
            hiHatClosed: new Tone.MetalSynth().toDestination(),
            clap: {
                synth: new Tone.NoiseSynth(),
                filter: new Tone.Filter(800, 'bandpass').toDestination()
            },
            cowbell: new Tone.MetalSynth().toDestination()
            // Weitere Drum-Sounds können hier hinzugefügt werden
        };

        // Snare und Clap Synthesizer mit Filter verbinden
        drumKit.snare.synth.connect(drumKit.snare.filter);
        drumKit.clap.synth.connect(drumKit.clap.filter);

        // Standardparameter für jedes Instrument
        const drumDefaults = {
            kick: { level: 0.8, tone: 60, decay: 0.5, tuning: 0 },
            snare: { level: 0.5, tone: 1000, decay: 0.2, tuning: 0 },
            hiHatClosed: { level: 0.5, tone: 1000, decay: 0.1, tuning: 0 },
            clap: { level: 0.5, tone: 800, decay: 0.2, tuning: 0 },
            cowbell: { level: 0.5, tone: 800, decay: 0.2, tuning: 0 }
        };

        // Drum Pattern
        const drumParts = {};
        const drumInstruments = ['kick', 'snare', 'hiHatClosed', 'clap', 'cowbell'];
        const drumStepsContainer = document.getElementById('drumSteps');

        drumInstruments.forEach(instrument => {
            const drumRow = document.createElement('div');
            drumRow.className = 'drum-row';

            // Instrumentenlabel
            const instrumentLabel = document.createElement('div');
            instrumentLabel.className = 'drum-label';
            instrumentLabel.textContent = instrument;
            drumRow.appendChild(instrumentLabel);

            // Kontrollelemente für jedes Instrument
            const controlsDiv = document.createElement('div');
            controlsDiv.className = 'drum-controls';

            controlsDiv.innerHTML = `
                <div class="drum-control-group">
                    <label>Level</label>
                    <input type="range" class="levelControl" min="0" max="1" step="0.01" value="${drumDefaults[instrument].level}">
                    <div class="levelValue">${drumDefaults[instrument].level}</div>
                </div>
                <div class="drum-control-group">
                    <label>Tone</label>
                    <input type="range" class="toneControl" min="100" max="5000" step="1" value="${drumDefaults[instrument].tone}">
                    <div class="toneValue">${drumDefaults[instrument].tone}</div>
                </div>
                <div class="drum-control-group">
                    <label>Decay</label>
                    <input type="range" class="decayControl" min="0.1" max="2" step="0.01" value="${drumDefaults[instrument].decay}">
                    <div class="decayValue">${drumDefaults[instrument].decay}</div>
                </div>
                <div class="drum-control-group">
                    <label>Tuning</label>
                    <input type="range" class="tuningControl" min="-12" max="12" step="1" value="${drumDefaults[instrument].tuning}">
                    <div class="tuningValue">${drumDefaults[instrument].tuning}</div>
                </div>
            `;
            drumRow.appendChild(controlsDiv);

            // Sequencer Steps für jedes Instrument
            const stepsContainer = document.createElement('div');
            stepsContainer.className = 'drum-steps';

            const steps = [];
            for (let i = 0; i < 16; i++) {
                const stepDiv = document.createElement('div');
                stepDiv.className = 'drum-step';

                stepDiv.innerHTML = `
                    <input type="checkbox" class="drum-toggle">
                `;
                stepsContainer.appendChild(stepDiv);
                const step = {
                    stepDiv: stepDiv,
                    toggle: stepDiv.querySelector('.drum-toggle')
                };

                steps.push(step);
            }

            drumRow.appendChild(stepsContainer);
            drumStepsContainer.appendChild(drumRow);

            // Event Listener für Kontrollelemente
            const levelControl = controlsDiv.querySelector('.levelControl');
            const levelValue = controlsDiv.querySelector('.levelValue');
            const toneControl = controlsDiv.querySelector('.toneControl');
            const toneValue = controlsDiv.querySelector('.toneValue');
            const decayControl = controlsDiv.querySelector('.decayControl');
            const decayValue = controlsDiv.querySelector('.decayValue');
            const tuningControl = controlsDiv.querySelector('.tuningControl');
            const tuningValue = controlsDiv.querySelector('.tuningValue');

            levelControl.addEventListener('input', function() {
                levelValue.textContent = levelControl.value;
                updateDrumSettings(instrument);
            });
            toneControl.addEventListener('input', function() {
                toneValue.textContent = toneControl.value;
                updateDrumSettings(instrument);
            });
            decayControl.addEventListener('input', function() {
                decayValue.textContent = decayControl.value;
                updateDrumSettings(instrument);
            });
            tuningControl.addEventListener('input', function() {
                tuningValue.textContent = tuningControl.value;
                updateDrumSettings(instrument);
            });

            // Drum Part erstellen
            drumParts[instrument] = {
                steps: steps,
                part: new Tone.Sequence(function(time, step){
                    if (step.toggle.checked) {
                        playDrumSound(instrument, time);
                    }
                    // Visuelle Anzeige des aktuellen Schritts
                    const index = Math.floor(drumParts[instrument].part.progress * 16);
                    drumParts[instrument].steps.forEach((s, i) => {
                        s.stepDiv.classList.toggle('current', i === index);
                    });
                }, steps, '16n'),
                controls: {
                    levelControl,
                    toneControl,
                    decayControl,
                    tuningControl
                }
            };

            // Initiale Einstellungen anwenden
            updateDrumSettings(instrument);
        });

        function updateDrumSettings(instrument) {
            const level = parseFloat(drumParts[instrument].controls.levelControl.value);
            const tone = parseFloat(drumParts[instrument].controls.toneControl.value);
            const decay = parseFloat(drumParts[instrument].controls.decayControl.value);
            const tuning = parseInt(drumParts[instrument].controls.tuningControl.value);

            let synth = drumKit[instrument];

            // Einstellungen je nach Instrument anwenden
            if (instrument === 'kick') {
                // Lautstärke anpassen
                synth.volume.value = (level * 30) - 24; // Level 0 -> -24dB, Level 1 -> +6dB
                synth.envelope.attack = 0.01;
                synth.envelope.decay = decay;
                synth.envelope.sustain = 0.5;
                synth.envelope.release = 0.1;
                synth.oscillator.pitchDecay = 0.05;
                synth.oscillator.octaves = 4;
                synth.frequency.value = Tone.Frequency('C2').transpose(tuning);
            } else if (instrument === 'snare' || instrument === 'clap') {
                synth.synth.volume.value = (level * 30) - 24;
                synth.synth.envelope.decay = decay;
                synth.filter.frequency.value = tone;
            } else if (instrument === 'hiHatClosed' || instrument === 'cowbell') {
                synth.volume.value = (level * 30) - 24;
                synth.envelope.decay = decay;
                synth.frequency.value = tone;
            }
        }

        function playDrumSound(instrument, time) {
            let synth = drumKit[instrument];

            if (instrument === 'kick') {
                // Aktuelle Frequenz verwenden
                synth.triggerAttackRelease(synth.frequency.value, '8n', time);
            } else if (instrument === 'snare' || instrument === 'clap') {
                synth.synth.triggerAttackRelease('16n', time);
            } else if (instrument === 'hiHatClosed' || instrument === 'cowbell') {
                synth.triggerAttackRelease('16n', time);
            }
        }

        // Drum BPM Kontrolle
        const drumBpmControl = document.getElementById('drumBpm');
        const drumBpmValue = document.getElementById('drumBpmValue');

        drumBpmControl.addEventListener('input', function() {
            drumBpmValue.textContent = drumBpmControl.value;
            Tone.Transport.bpm.value = drumBpmControl.value;
        });

        let isDrumPlaying = false;

        document.getElementById('drumPlayButton').addEventListener('click', async function() {
            if (!isDrumPlaying) {
                await Tone.start();

                // Transport konfigurieren
                Tone.Transport.bpm.value = drumBpmControl.value;
                Tone.Transport.start();

                // Drum Parts starten
                for (let instrument in drumParts) {
                    drumParts[instrument].part.start(0);
                }

                isDrumPlaying = true;
                this.textContent = 'Stop TR-808';
            } else {
                Tone.Transport.stop();

                // Drum Parts stoppen
                for (let instrument in drumParts) {
                    drumParts[instrument].part.stop();
                    // Aktuelle Schritte zurücksetzen
                    drumParts[instrument].steps.forEach(step => {
                        step.stepDiv.classList.remove('current');
                    });
                }

                isDrumPlaying = false;
                this.textContent = 'Play TR-808';
            }
        });

        // Hinweis: Wenn du möchtest, dass TB-303 und TR-808 synchron laufen, entferne die separaten Tone.Transport.start() und stop() Aufrufe und starte beide Sequenzen zusammen.
    
    </script>
</body>
</html>

Leave a Reply

Copyright SeroSync All Rights Reserved