Interactive Audio Analysis with Adjustable Color Mapping

Upload an audio file or activate the microphone to visualize audio features and customize the color mapping based on audio characteristics.

2048

Waveform (Volume vs. Time)

Time (s)

Frequency Analysis (FFT)

Frequency (Hz)

Average Frequency: 0 Hz

Real-Time Volume: 0

Custom Color Mapping

1
1
50%

HSL Color Value: HSL(0, 0%, 50%)

Getting Started

1. Upload an audio file or click "Activate Microphone" to visualize audio data in real-time.

2. Observe the waveform and frequency analysis visualizations.

3. Adjust the sliders to map audio frequency and volume data to HSL color values for custom visual effects.

Controls Explained

var coll = document.getElementsByClassName("collapsible"); var i; for (i = 0; i< coll.length; i++) { coll[i].addEventListener("click", function() { this.classList.toggle("active"); var content = this.nextElementSibling;if(content.style.display === "block") { content.style.display = "none"; } else { content.style.display = "block"; } }); } const audioUpload = document.getElementById('audio-upload'); const micToggle = document.getElementById('mic-toggle'); const fftSizeControl = document.getElementById('fft-size'); const fftSizeDisplay = document.getElementById('fft-size-value'); const waveformCanvas = document.getElementById('waveformCanvas'); const fftCanvas = document.getElementById('fftCanvas'); const averageFrequencyDisplay = document.getElementById('averageFrequency'); const realTimeVolumeDisplay = document.getElementById('realTimeVolume'); const colorDisplay = document.getElementById('colorDisplay'); const hslValueDisplay = document.getElementById('hslValue'); const waveformCtx = waveformCanvas.getContext('2d'); const fftCtx = fftCanvas.getContext('2d'); const hueMultiplierControl = document.getElementById('hue-multiplier'); const saturationMultiplierControl = document.getElementById('saturation-multiplier'); const lightnessControl = document.getElementById('lightness-value'); const hueMultiplierDisplay = document.getElementById('hue-multiplier-value'); const saturationMultiplierDisplay = document.getElementById('saturation-multiplier-value'); let audioContext, audioSource, analyser, dataArray, bufferLength; let micStream = null; fftSizeControl.addEventListener('input', (event) => { fftSizeDisplay.textContent = event.target.value; if (analyser) { analyser.fftSize = parseInt(event.target.value); bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); } }); hueMultiplierControl.addEventListener('input', () => { hueMultiplierDisplay.textContent = hueMultiplierControl.value; }); saturationMultiplierControl.addEventListener('input', () => { saturationMultiplierDisplay.textContent = saturationMultiplierControl.value; }); lightnessControl.addEventListener('input', () => { lightnessDisplay.textContent = lightnessControl.value + '%'; }); audioUpload.addEventListener('change', (event) => { if (audioContext) audioContext.close(); audioContext = new (window.AudioContext || window.webkitAudioContext)(); if (micStream) { micStream.getTracks().forEach(track => track.stop()); } const file = event.target.files[0]; const fileReader = new FileReader(); fileReader.onload = (e) => { audioContext.decodeAudioData(e.target.result, (buffer) => { audioSource = audioContext.createBufferSource(); audioSource.buffer = buffer; analyser = audioContext.createAnalyser(); analyser.fftSize = parseInt(fftSizeControl.value); bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength);audioSource.connect(analyser); analyser.connect(audioContext.destination); audioSource.start(); drawWaveform(); drawFFT(); updateColor(); }); }; fileReader.readAsArrayBuffer(file); }); micToggle.addEventListener('click', () => { if (audioContext) audioContext.close(); audioContext = new (window.AudioContext || window.webkitAudioContext)(); navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { micStream = stream; audioSource = audioContext.createMediaStreamSource(stream); analyser = audioContext.createAnalyser(); analyser.fftSize = parseInt(fftSizeControl.value); bufferLength=analyser.frequencyBinCount; dataArray=new Uint8Array(bufferLength);audioSource.connect(analyser); drawWaveform(); drawFFT(); updateColor(); }) .catch(error=>{ console.error("Microphone access denied or not supported", error); }); }); function drawWaveform() { requestAnimationFrame(drawWaveform); analyser.getByteTimeDomainData(dataArray); waveformCtx.clearRect(0,0,waveformCanvas.width,waveformCanvas.height); waveformCtx.beginPath(); const sliceWidth=waveformCanvas.width /bufferLength; let x=0; for(let i=0;i0) count++; } const avgFreq=count ?totalFrequency /count :0; averageFrequencyDisplay.textContent=avgFreq.toFixed(2); updateColor(); } function updateColor() { const avgFreq=parseFloat(averageFrequencyDisplay.textContent); const volume=dataArray.reduce((acc,val)=> acc + val,0) /dataArray.length; realTimeVolumeDisplay.textContent=volume.toFixed(2); const hue= avgFreq * hueMultiplierControl.value; const saturation= volume * saturationMultiplierControl.value; const lightness= lightnessControl.value;colorDisplay.style.backgroundColor=`hsl(${hue %360}, ${Math.min(saturation,100)}%, ${lightness}%)`; hslValueDisplay.textContent=`HSL(${(hue %360).toFixed(1)}, ${Math.min(saturation,100).toFixed(1)}%, ${lightness}%)`; }

Understanding FFT and HSL Conversion

Fast Fourier Transform (FFT):

The FFT is used to convert a signal from its original time domain into a frequency domain. The formula for the discrete Fourier transform is:\[ X_k=\sum_{n=0}^{N-1}x_n \cdot e^{-i \frac{2\pi}{N} kn} \]where \(X_k\) is the output complex number representing amplitude and phase at frequency \(k\), \(x_n\) is the input time-domain signal, \(N\) is the number of samples,and \(i\) is the imaginary unit.

HSL Conversion:

- Hue (\(H\)):Calculated from the average frequency:\[ H=\left(\frac{\text{Average Frequency}}{\text{Max Frequency}}\right)\times360 \]-Saturation (\(S\)):Based on volume:\[ S=\left(\frac{\text{Volume}}{\text{Max Volume}}\right)\times100 \]-Lightness (\(L\)):Set at a constant value of \(50\%\).