Please bookmark this page to avoid losing your image tool!

Photo To Audio Converter

(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.
function processImage(originalImg, durationSeconds = "5", minFrequency = "100", maxFrequency = "10000", timeResolution = "300", freqResolution = "100") {
    const durationSec = Number(durationSeconds);
    const minFreq = Number(minFrequency);
    const maxFreq = Number(maxFrequency);
    const timeResN = Math.max(1, Number(timeResolution));
    const freqResN = Math.max(1, Number(freqResolution));

    // Create the container UI
    const container = document.createElement('div');
    container.style.fontFamily = 'system-ui, sans-serif';
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.gap = '20px';
    container.style.padding = '30px';
    container.style.background = '#1a1a1a';
    container.style.color = '#ffffff';
    container.style.borderRadius = '12px';
    container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.4)';
    container.style.width = '100%';
    container.style.boxSizing = 'border-box';
    
    // Initial loading state UI
    const title = document.createElement('div');
    title.textContent = 'Preparing Audio Generator...';
    title.style.fontWeight = 'bold';
    title.style.fontSize = '20px';
    container.appendChild(title);

    // Process asynchronously to avoid freezing the browser interface
    setTimeout(async () => {
        title.textContent = 'Extracting Image Data...';

        const sampleRate = 44100;
        const N = Math.floor(durationSec * sampleRate);
        const buffer = new Float32Array(N);

        const ctx = document.createElement('canvas').getContext('2d', { willReadFrequently: true });
        ctx.canvas.width = timeResN;
        ctx.canvas.height = freqResN;
        
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, timeResN, freqResN);
        ctx.drawImage(originalImg, 0, 0, timeResN, freqResN);
        const imgData = ctx.getImageData(0, 0, timeResN, freqResN).data;

        const luminanceGrid = new Float32Array(freqResN * timeResN);
        for (let y = 0; y < freqResN; y++) {
            for (let x = 0; x < timeResN; x++) {
                const idx = (y * timeResN + x) * 4;
                let r = imgData[idx] / 255;
                let g = imgData[idx + 1] / 255;
                let b = imgData[idx + 2] / 255;
                
                // Emphasize brightness using non-linear scale (squared) to reduce noise floor
                let lum = (r * 0.299 + g * 0.587 + b * 0.114);
                luminanceGrid[y * timeResN + x] = lum * lum; 
            }
        }

        title.textContent = 'Synthesizing Spectrogram...';
        
        const progressBarWrap = document.createElement('div');
        progressBarWrap.style.width = '100%';
        progressBarWrap.style.maxWidth = '600px';
        progressBarWrap.style.height = '12px';
        progressBarWrap.style.background = '#333';
        progressBarWrap.style.borderRadius = '6px';
        progressBarWrap.style.overflow = 'hidden';
        
        const progressBar = document.createElement('div');
        progressBar.style.width = '0%';
        progressBar.style.height = '100%';
        progressBar.style.background = '#e74c3c';
        progressBar.style.transition = 'width 0.1s linear';
        
        progressBarWrap.appendChild(progressBar);
        container.appendChild(progressBarWrap);
        
        const xStep = (timeResN - 1) / N;
        const maxFreqIndex = Math.max(1, freqResN - 1);

        await new Promise(resolve => {
            let y = 0;
            // Process the generative synthesis in batches
            function processChunk() {
                const startY = y;
                const endY = Math.min(y + 5, freqResN); 
                
                for (; y < endY; y++) {
                    const freq = minFreq * Math.pow(maxFreq / minFreq, (maxFreqIndex - y) / maxFreqIndex);
                    let phase = 0;
                    const phaseInc = 2 * Math.PI * freq / sampleRate;
                    const rowOffset = y * timeResN;

                    for (let i = 0; i < N; i++) {
                        const x = i * xStep;
                        const x0 = Math.floor(x);
                        const x1 = Math.min(x0 + 1, timeResN - 1);
                        const frac = x - x0;

                        const lum0 = luminanceGrid[rowOffset + x0];
                        const lum1 = luminanceGrid[rowOffset + x1];

                        const amplitude = lum0 * (1 - frac) + lum1 * frac;
                        
                        buffer[i] += Math.sin(phase) * amplitude;
                        phase = (phase + phaseInc) % (2 * Math.PI);
                    }
                }
                
                progressBar.style.width = `${(y / freqResN) * 100}%`;
                
                if (y < freqResN) {
                    setTimeout(processChunk, 10);
                } else {
                    resolve();
                }
            }
            processChunk();
        });

        title.textContent = 'Finalizing Audio File...';

        // Normalize buffer
        let maxAmp = 0;
        for (let i = 0; i < N; i++) {
            const absVal = Math.abs(buffer[i]);
            if (absVal > maxAmp) maxAmp = absVal;
        }

        if (maxAmp > 0) {
            for (let i = 0; i < N; i++) {
                buffer[i] = (buffer[i] / maxAmp) * 0.9;
            }
        }

        // WAV Header Generation (16-bit PCM Mono)
        const bufferLength = buffer.length;
        const wavFile = new Uint8Array(44 + bufferLength * 2);
        const view = new DataView(wavFile.buffer);

        const writeString = (view, offset, string) => {
            for (let i = 0; i < string.length; i++) {
                view.setUint8(offset + i, string.charCodeAt(i));
            }
        };

        writeString(view, 0, 'RIFF');
        view.setUint32(4, 36 + bufferLength * 2, true);
        writeString(view, 8, 'WAVE');
        writeString(view, 12, 'fmt ');
        view.setUint32(16, 16, true);
        view.setUint16(20, 1, true); // PCM Format
        view.setUint16(22, 1, true); // Mono
        view.setUint32(24, sampleRate, true);
        view.setUint32(28, sampleRate * 2, true); // Byte rate
        view.setUint16(32, 2, true); // Block align
        view.setUint16(34, 16, true); // Bits per sample
        writeString(view, 36, 'data');
        view.setUint32(40, bufferLength * 2, true);

        // PCM Writing
        let offset = 44;
        for (let i = 0; i < bufferLength; i++) {
            let s = Math.max(-1, Math.min(1, buffer[i]));
            view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
            offset += 2;
        }

        const blob = new Blob([view], { type: 'audio/wav' });
        const audioUrl = URL.createObjectURL(blob);

        container.innerHTML = '';

        // Switch to the Audio Player user interface
        const finalTitle = document.createElement('div');
        finalTitle.textContent = 'Audio Output Ready';
        finalTitle.style.fontWeight = 'bold';
        finalTitle.style.fontSize = '20px';
        finalTitle.style.marginBottom = '10px';
        container.appendChild(finalTitle);

        const canvas = document.createElement('canvas');
        canvas.width = originalImg.width;
        canvas.height = originalImg.height;
        canvas.style.maxWidth = '100%';
        canvas.style.maxHeight = '450px';
        canvas.style.background = '#000';
        canvas.style.borderRadius = '8px';
        canvas.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.4)';
        
        const outCtx = canvas.getContext('2d');
        outCtx.drawImage(originalImg, 0, 0);

        const audioEle = document.createElement('audio');
        audioEle.controls = true;
        audioEle.src = audioUrl;
        audioEle.style.width = '100%';
        audioEle.style.maxWidth = '600px';
        audioEle.style.marginTop = '20px';

        let isPlaying = false;
        let animationFrameId;

        function drawPlayhead() {
            outCtx.drawImage(originalImg, 0, 0);
            
            let duration = audioEle.duration;
            if (!duration || isNaN(duration)) duration = durationSec;

            let progress = audioEle.currentTime / duration;
            if (progress > 0 && progress <= 1) {
                const x = progress * canvas.width;
                outCtx.beginPath();
                outCtx.moveTo(x, 0);
                outCtx.lineTo(x, canvas.height);
                outCtx.lineWidth = Math.max(2, canvas.width / 400);
                outCtx.strokeStyle = 'rgba(255, 75, 75, 0.9)';
                outCtx.shadowColor = 'rgba(0, 0, 0, 0.5)';
                outCtx.shadowBlur = 5;
                outCtx.stroke();
                
                // Reset shadow values immediately
                outCtx.shadowColor = 'transparent';
                outCtx.shadowBlur = 0;
            }
        }

        function renderFrame() {
            if (!isPlaying) return;
            drawPlayhead();
            animationFrameId = requestAnimationFrame(renderFrame);
        }

        audioEle.addEventListener('play', () => {
            isPlaying = true;
            renderFrame();
        });

        audioEle.addEventListener('pause', () => {
            isPlaying = false;
            cancelAnimationFrame(animationFrameId);
            drawPlayhead();
        });

        audioEle.addEventListener('seeked', () => {
            drawPlayhead();
        });

        audioEle.addEventListener('ended', () => {
            isPlaying = false;
            cancelAnimationFrame(animationFrameId);
            outCtx.drawImage(originalImg, 0, 0);
        });

        container.appendChild(canvas);
        container.appendChild(audioEle);

    }, 100);

    return container;
}

Free Image Tool Creator

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

Description

The Photo To Audio Converter is a tool that transforms images into sound by interpreting visual data as a spectrogram. It analyzes the brightness and luminance of an image to synthesize audio frequencies, effectively turning a static picture into a unique auditory experience. This tool can be used for creative digital art projects, experimental sound design, or as a unique way to ‘hear’ a visual composition.

Leave a Reply

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