Please bookmark this page to avoid losing your image tool!

Image Audio Pitch Visualizer

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, audioUrl = '', lineColor = 'rgba(255, 255, 0, 0.8)', lineWidth = 2, minFreq = 80, maxFreq = 1200) {

    /**
     * Finds the fundamental frequency (pitch) of an audio signal frame.
     * @param {Float32Array} frame - A small chunk of audio data.
     * @param {number} sampleRate - The sample rate of the audio.
     * @returns {number|null} The detected frequency in Hz, or null if not confident.
     */
    const getPitchFromFrame = (frame, sampleRate) => {
        const frameSize = frame.length;
        const confidenceThreshold = 0.9; // How similar the signal is to its shifted self.

        // 1. Calculate the Auto-Correlation Function (ACF) for the frame.
        // The ACF measures the similarity between a signal and a time-shifted version of itself.
        // A peak in the ACF corresponds to the period of a repeating pattern.
        const acf = new Float32Array(frameSize).fill(0);
        for (let lag = 0; lag < frameSize; lag++) {
            for (let i = 0; i < frameSize - lag; i++) {
                acf[lag] += frame[i] * frame[i + lag];
            }
        }

        // 2. Normalize the ACF.
        // We divide by the energy at lag 0 to get values between -1 and 1.
        const acf0 = acf[0];
        if (acf0 < 0.0001) return null; // Silence
        for (let i = 0; i < frameSize; i++) {
            acf[i] /= acf0;
        }

        // 3. Find the first significant peak.
        // We skip the peak at lag 0 (which is always 1) and find the next major peak.
        // This peak's position (lag) indicates the signal's fundamental period.
        let d = 0;
        while (d < frameSize && acf[d] > 0.95) { // Find where the initial peak drops off
            d++;
        }
        if (d === frameSize) return null;

        let maxVal = -1;
        let maxLag = -1;
        for (let lag = d; lag < frameSize; lag++) {
            if (acf[lag] > maxVal) {
                maxVal = acf[lag];
                maxLag = lag;
            }
        }
        
        // 4. Check if the peak is strong enough (confidence) and return the frequency.
        // Frequency = Sample Rate / Period (where Period is the lag of the peak).
        if (maxVal > confidenceThreshold && maxLag > 0) {
            // Use parabolic interpolation to get a more accurate fractional lag.
            if (maxLag > 0 && maxLag < frameSize - 1) {
                const y1 = acf[maxLag - 1];
                const y2 = acf[maxLag];
                const y3 = acf[maxLag + 1];
                const correction = 0.5 * (y3 - y1) / (y1 - 2 * y2 + y3);
                maxLag += correction;
            }
            return sampleRate / maxLag;
        }

        return null; // No confident pitch found
    };

    // 1. Setup Canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(originalImg, 0, 0);

    if (!audioUrl) {
        console.error("No audio URL provided.");
        ctx.fillStyle = 'red';
        ctx.font = '20px sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText('No audio URL provided', canvas.width / 2, canvas.height / 2);
        return canvas;
    }
    
    // Use a try-catch block to handle potential network or decoding errors.
    try {
        // 2. Fetch and Decode Audio Data using the Web Audio API
        const AudioContext = window.AudioContext || window.webkitAudioContext;
        if (!AudioContext) {
             throw new Error("Web Audio API is not supported in this browser.");
        }
        const audioContext = new AudioContext();
        const response = await fetch(audioUrl);
        const arrayBuffer = await response.arrayBuffer();
        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

        // 3. Analyze Audio to extract pitch over time
        const pcmData = audioBuffer.getChannelData(0); // Use the first channel
        const sampleRate = audioBuffer.sampleRate;
        const frameSize = 2048; // Size of each audio chunk to analyze
        const hopSize = 441;   // How far to slide the analysis window for the next frame
        
        const pitches = [];
        for (let i = 0; i + frameSize <= pcmData.length; i += hopSize) {
            const frame = pcmData.subarray(i, i + frameSize);
            const freq = getPitchFromFrame(frame, sampleRate);
            const time = i / sampleRate;
            pitches.push({ time, freq });
        }

        // 4. Draw the Pitch Visualization on the Canvas
        const duration = audioBuffer.duration;
        const pitchRange = maxFreq - minFreq;

        if (pitchRange <= 0) {
           throw new Error("maxFreq must be greater than minFreq.");
        }

        ctx.strokeStyle = lineColor;
        ctx.lineWidth = lineWidth;
        ctx.lineCap = 'round';
        ctx.lineJoin = 'round';
        ctx.beginPath();
        
        let isLineStarted = false;
        for (const point of pitches) {
             // Only draw points with a valid frequency within the specified range
            if (point.freq !== null && point.freq >= minFreq && point.freq <= maxFreq) {
                const x = (point.time / duration) * canvas.width;
                const y = canvas.height - ((point.freq - minFreq) / pitchRange) * canvas.height;

                if (!isLineStarted) {
                    ctx.moveTo(x, y);
                    isLineStarted = true;
                } else {
                    ctx.lineTo(x, y);
                }
            } else {
                 // If there's a gap (unpitched sound), stop the current line segment
                isLineStarted = false;
            }
        }
        ctx.stroke();

    } catch (error) {
        console.error("Error processing audio:", error);
        ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
        ctx.font = 'bold 16px sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText(`Error loading audio: ${error.message}`, canvas.width / 2, canvas.height / 2);
    }

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Audio Pitch Visualizer is a tool that combines visual and audio data to create a pitch visualization based on an audio file. Users can upload an image and provide an audio URL, and the tool will analyze the audio to detect pitches over time, visualizing these frequencies directly onto the image in a customizable way. This can be useful for musicians, educators, or anyone interested in visualizing sound patterns, as it provides a clear representation of audio pitches against a background image.

Leave a Reply

Your email address will not be published. Required fields are marked *