Welcome! Share code as fast as possible.

import React, { useState, useEffect, useCallback } from 'react';

const EightBitSequencer = () => {
  // Audio context setup
  const [audioContext, setAudioContext] = useState(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [bpm, setBpm] = useState(120);
  const [currentStep, setCurrentStep] = useState(0);
  const [masterVolume, setMasterVolume] = useState(0.2);

  // Sequence state
  const [sequence, setSequence] = useState({
    kick: Array(16).fill(false),
    snare: Array(16).fill(false),
    hihat: Array(16).fill(false),
    bass: Array(16).fill(false),
    lead1: Array(16).fill(false),
    lead2: Array(16).fill(false),
    lead3: Array(16).fill(false) // Added third lead
  });

  // Note frequencies (C major pentatonic scale)
  const noteFrequencies = {
    'C3': 130.81, 'D3': 146.83, 'E3': 164.81, 'G3': 196.00, 'A3': 220.00,
    'C4': 261.63, 'D4': 293.66, 'E4': 329.63, 'G4': 392.00, 'A4': 440.00,
    'C5': 523.25, 'D5': 587.33, 'E5': 659.26, 'G5': 783.99, 'A5': 880.00,
  };

  // Instrument note assignments
  const [instrumentNotes, setInstrumentNotes] = useState({
    bass: ['C3', 'D3', 'E3', 'G3', 'A3'],
    lead1: ['C4', 'D4', 'E4', 'G4', 'A4'],
    lead2: ['E4', 'G4', 'A4', 'C5', 'D5'],
    lead3: ['G4', 'A4', 'C5', 'D5', 'E5'],
  });

  // Note selection for melodic instruments
  const [selectedNotes, setSelectedNotes] = useState({
    bass: Array(16).fill('C3'),
    lead1: Array(16).fill('C4'),
    lead2: Array(16).fill('E4'),
    lead3: Array(16).fill('G4'),
  });

  // Initialize audio context
  useEffect(() => {
    const initAudio = () => {
      const context = new (window.AudioContext || window.webkitAudioContext)();
      setAudioContext(context);
    };

    if (!audioContext) {
      initAudio();
    }

    return () => {
      if (audioContext) {
        audioContext.close();
      }
    };
  }, [audioContext]);

  // Create a master gain node
  const [masterGainNode, setMasterGainNode] = useState(null);

  useEffect(() => {
    if (audioContext && !masterGainNode) {
      const gainNode = audioContext.createGain();
      gainNode.gain.value = masterVolume;
      gainNode.connect(audioContext.destination);
      setMasterGainNode(gainNode);
    }
  }, [audioContext, masterGainNode]);

  // Update master volume
  useEffect(() => {
    if (masterGainNode) {
      masterGainNode.gain.value = masterVolume;
    }
  }, [masterVolume, masterGainNode]);

  // Create an oscillator for melodic sounds with limited volume
  const createOscillator = useCallback((frequency, type, duration, time, detune = 0, volumeMultiplier = 1) => {
    if (!audioContext || !masterGainNode) return;
    
    const osc = audioContext.createOscillator();
    const gainNode = audioContext.createGain();
    
    osc.type = type;
    osc.frequency.value = frequency;
    osc.detune.value = detune;
    
    // Adjust volume based on instrument type
    const baseVolume = 0.1 * masterVolume * volumeMultiplier;
    
    // Attack
    gainNode.gain.setValueAtTime(0, time);
    gainNode.gain.linearRampToValueAtTime(baseVolume, time + 0.01);
    
    // Release
    gainNode.gain.setValueAtTime(baseVolume, time + duration - 0.05);
    gainNode.gain.linearRampToValueAtTime(0, time + duration);
    
    osc.connect(gainNode).connect(masterGainNode);
    osc.start(time);
    osc.stop(time + duration);
  }, [audioContext, masterGainNode, masterVolume]);

  // Create noise for percussion with limited volume
  const createNoise = useCallback((duration, time, frequency = 100, volumeMultiplier = 1) => {
    if (!audioContext || !masterGainNode) return;
    
    const bufferSize = audioContext.sampleRate * duration;
    const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate);
    const data = buffer.getChannelData(0);
    
    for (let i = 0; i < bufferSize; i++) {
      data[i] = (Math.random() * 2 - 1) * 0.3;
    }
    
    const noise = audioContext.createBufferSource();
    noise.buffer = buffer;
    
    const gainNode = audioContext.createGain();
    const filter = audioContext.createBiquadFilter();
    
    filter.type = 'highpass';
    filter.frequency.value = frequency;
    
    // Adjust volume for percussion
    const baseVolume = 0.08 * masterVolume * volumeMultiplier;
    
    gainNode.gain.setValueAtTime(baseVolume, time);
    gainNode.gain.exponentialRampToValueAtTime(0.001, time + duration);
    
    noise.connect(filter).connect(gainNode).connect(masterGainNode);
    noise.start(time);
  }, [audioContext, masterGainNode, masterVolume]);

  // Play sounds for the current step
  const playSounds = useCallback((step) => {
    if (!audioContext) return;
    
    const now = audioContext.currentTime;
    
    // Kick drum - increased volume
    if (sequence.kick[step]) {
      createOscillator(50, 'sine', 0.1, now, 0, 1.5);
      createOscillator(40, 'sine', 0.2, now, 0, 1.5);
    }
    
    // Snare - increased volume
    if (sequence.snare[step]) {
      createNoise(0.1, now, 800, 1.5);
      createOscillator(150, 'triangle', 0.05, now, 0, 1.5);
    }
    
    // Hi-hat - increased volume
    if (sequence.hihat[step]) {
      createNoise(0.05, now, 8000, 1.5);
    }
    
    // Bass - reduced volume
    if (sequence.bass[step]) {
      const note = selectedNotes.bass[step];
      createOscillator(noteFrequencies[note], 'square', 0.15, now, 0, 0.6);
      createOscillator(noteFrequencies[note] / 2, 'sine', 0.15, now, 0, 0.6);
    }
    
    // Lead 1 - square wave
    if (sequence.lead1[step]) {
      const note = selectedNotes.lead1[step];
      createOscillator(noteFrequencies[note], 'square', 0.1, now);
      createOscillator(noteFrequencies[note], 'square', 0.1, now, 5);
    }
    
    // Lead 2 - triangle/sine wave mix
    if (sequence.lead2[step]) {
      const note = selectedNotes.lead2[step];
      createOscillator(noteFrequencies[note], 'triangle', 0.1, now);
      createOscillator(noteFrequencies[note] * 2, 'sine', 0.05, now, -5);
    }
    
    // Lead 3 - sawtooth wave for brighter sound
    if (sequence.lead3[step]) {
      const note = selectedNotes.lead3[step];
      createOscillator(noteFrequencies[note], 'sawtooth', 0.1, now);
      createOscillator(noteFrequencies[note] * 1.5, 'sawtooth', 0.08, now, 7, 0.4);
    }
  }, [sequence, selectedNotes, createOscillator, createNoise, audioContext]);

  // Simple interval-based sequencer with correct timing
  useEffect(() => {
    let intervalId;
    
    if (isPlaying) {
      // Correctly calculate interval time in milliseconds
      const intervalTime = (60000 / bpm) / 4; // ms per 16th note
      
      intervalId = setInterval(() => {
        playSounds(currentStep);
        setCurrentStep((prev) => (prev + 1) % 16);
      }, intervalTime);
    }
    
    return () => {
      if (intervalId) {
        clearInterval(intervalId);
      }
    };
  }, [isPlaying, bpm, currentStep, playSounds]);

  // Toggle sequencer cell
  const toggleCell = (instrument, step) => {
    setSequence(prev => ({
      ...prev,
      [instrument]: prev[instrument].map((value, i) => 
        i === step ? !value : value
      )
    }));
  };

  // Change note for melodic instruments
  const changeNote = (instrument, step, note) => {
    setSelectedNotes(prev => ({
      ...prev,
      [instrument]: prev[instrument].map((value, i) => 
        i === step ? note : value
      )
    }));
  };
  
  // Toggle playback
  const togglePlayback = () => {
    if (!isPlaying && audioContext && audioContext.state === "suspended") {
      audioContext.resume();
    }
    setIsPlaying(!isPlaying);
    setCurrentStep(0);
  };

  // Clear all sequences
  const clearAll = () => {
    setSequence({
      kick: Array(16).fill(false),
      snare: Array(16).fill(false),
      hihat: Array(16).fill(false),
      bass: Array(16).fill(false),
      lead1: Array(16).fill(false),
      lead2: Array(16).fill(false),
      lead3: Array(16).fill(false)
    });
  };

  // Load demo pattern
  const loadDemo = () => {
    setSequence({
      kick: [true, false, false, false, true, false, false, false, true, false, false, false, true, false, false, false],
      snare: [false, false, true, false, false, false, true, false, false, false, true, false, false, false, true, false],
      hihat: [true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false],
      bass: [true, false, false, false, false, false, true, false, false, false, false, true, false, false, true, false],
      lead1: [false, false, true, false, true, false, false, false, false, false, true, false, true, false, false, false],
      lead2: [false, false, false, false, false, false, false, true, true, false, false, false, false, true, false, false],
      lead3: [false, true, false, false, false, false, true, false, false, false, false, false, true, false, false, true]
    });
    
    setSelectedNotes({
      bass: ['C3', 'C3', 'C3', 'C3', 'C3', 'C3', 'E3', 'E3', 'G3', 'G3', 'G3', 'A3', 'A3', 'A3', 'C3', 'C3'],
      lead1: ['C4', 'C4', 'E4', 'E4', 'G4', 'G4', 'G4', 'G4', 'C4', 'C4', 'E4', 'E4', 'G4', 'G4', 'G4', 'G4'],
      lead2: ['E4', 'E4', 'E4', 'E4', 'E4', 'E4', 'E4', 'A4', 'G4', 'G4', 'G4', 'G4', 'G4', 'A4', 'G4', 'G4'],
      lead3: ['G4', 'A4', 'C5', 'C5', 'C5', 'C5', 'A4', 'G4', 'G4', 'G4', 'G4', 'G4', 'E5', 'D5', 'C5', 'G4']
    });
  };

  // Get cell color based on state
  const getCellColor = (instrument, step) => {
    if (currentStep === step && isPlaying) {
      return sequence[instrument][step] ? 'bg-yellow-300' : 'bg-yellow-100';
    }
    return sequence[instrument][step] ? getInstrumentColor(instrument) : 'bg-gray-200';
  };

  // Instrument-specific colors
  const getInstrumentColor = (instrument) => {
    switch(instrument) {
      case 'kick': return 'bg-red-500';
      case 'snare': return 'bg-orange-500';
      case 'hihat': return 'bg-amber-500';
      case 'bass': return 'bg-blue-500';
      case 'lead1': return 'bg-purple-500';
      case 'lead2': return 'bg-green-500';
      case 'lead3': return 'bg-pink-500';
      default: return 'bg-gray-500';
    }
  };

  // Render note selector for melodic instruments
  const renderNoteSelector = (instrument, step) => {
    return (
      <select 
        className="text-xs p-1 w-full bg-gray-700 text-white border border-gray-600" 
        value={selectedNotes[instrument][step]} 
        onChange={(e) => changeNote(instrument, step, e.target.value)}
        disabled={!sequence[instrument][step]}
      >
        {instrumentNotes[instrument].map(note => (
          <option key={note} value={note}>{note}</option>
        ))}
      </select>
    );
  };

  return (
    <div className="w-full max-w-4xl mx-auto p-4 bg-gray-800 text-white rounded-lg shadow-lg">
      <h1 className="text-2xl font-bold text-center mb-4">8-Bit Sequencer</h1>
      
      <div className="flex items-center justify-between gap-4 mb-4">
        <div className="flex gap-2">
          <button 
            className={`px-4 py-2 rounded font-bold ${isPlaying ? 'bg-red-500' : 'bg-green-500'}`}
            onClick={togglePlayback}
          >
            {isPlaying ? 'Stop' : 'Play'}
          </button>
          <button className="px-4 py-2 bg-gray-600 rounded" onClick={clearAll}>
            Clear
          </button>
          <button className="px-4 py-2 bg-blue-600 rounded" onClick={loadDemo}>
            Demo
          </button>
        </div>
        
        <div className="flex items-center gap-2">
          <label>BPM:</label>
          <input 
            type="range" 
            min="60" 
            max="200" 
            value={bpm} 
            onChange={(e) => setBpm(parseInt(e.target.value))}
            className="w-24"
          />
          <span>{bpm}</span>
        </div>
        
        <div className="flex items-center gap-2">
          <label>Volume:</label>
          <input 
            type="range" 
            min="0.01" 
            max="0.5" 
            step="0.01" 
            value={masterVolume} 
            onChange={(e) => setMasterVolume(parseFloat(e.target.value))}
            className="w-24"
          />
        </div>
      </div>
      
      <div className="overflow-x-auto">
        <div className="grid grid-cols-17 gap-1 mb-6 min-w-max">
          {/* Column headers */}
          <div className="font-bold">Instrument</div>
          {Array(16).fill().map((_, i) => (
            <div key={i} className="text-center">{i + 1}</div>
          ))}
          
          {/* Percussion sequencer rows */}
          {['kick', 'snare', 'hihat'].map(instrument => (
            <React.Fragment key={instrument}>
              <div className="flex items-center">
                <div className={`w-4 h-4 rounded-full mr-1 ${getInstrumentColor(instrument)}`}></div>
                <span className="capitalize">{instrument}</span>
              </div>
              {Array(16).fill().map((_, step) => (
                <div 
                  key={step} 
                  className={`w-8 h-8 rounded cursor-pointer ${getCellColor(instrument, step)}`}
                  onClick={() => toggleCell(instrument, step)}
                ></div>
              ))}
            </React.Fragment>
          ))}
          
          {/* Melodic sequencer rows */}
          {['bass', 'lead1', 'lead2', 'lead3'].map(instrument => (
            <React.Fragment key={instrument}>
              <div className="flex items-center">
                <div className={`w-4 h-4 rounded-full mr-1 ${getInstrumentColor(instrument)}`}></div>
                <span className="capitalize">
                  {instrument === 'lead1' ? 'Lead 1' : 
                   instrument === 'lead2' ? 'Lead 2' : 
                   instrument === 'lead3' ? 'Lead 3' : instrument}
                </span>
              </div>
              {Array(16).fill().map((_, step) => (
                <div key={step} className="flex flex-col">
                  <div 
                    className={`w-8 h-8 rounded cursor-pointer ${getCellColor(instrument, step)}`}
                    onClick={() => toggleCell(instrument, step)}
                  ></div>
                  {sequence[instrument][step] && renderNoteSelector(instrument, step)}
                </div>
              ))}
            </React.Fragment>
          ))}
        </div>
      </div>
      
      <div className="mt-4 text-center text-sm text-gray-300">
        Click on grid cells to activate/deactivate notes. For melody instruments, select notes from the dropdown when activated.
      </div>
    </div>
  );
};

// Add this CSS to fix grid layout
const styles = document.createElement('style');
styles.innerHTML = `
.grid-cols-17 {
  grid-template-columns: 100px repeat(16, minmax(40px, 1fr));
}
`;
document.head.appendChild(styles);

export default EightBitSequencer;