Please bookmark this page to avoid losing your image tool!

Photo To Music Track 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, durationSec = 10, timeResolution = 200, frequencyBands = 80, minFrequency = 65, maxFrequency = 4000) {
    // Parse parameters
    const W = parseInt(timeResolution, 10) || 200;
    const H = parseInt(frequencyBands, 10) || 80;
    const duration = parseFloat(durationSec) || 10;
    const minFreq = parseFloat(minFrequency) || 65;
    const maxFreq = parseFloat(maxFrequency) || 4000;

    // Create main container
    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
    container.style.padding = '25px';
    container.style.gap = '20px';
    container.style.backgroundColor = '#1e1e24';
    container.style.color = '#ffffff';
    container.style.borderRadius = '12px';
    container.style.boxShadow = '0 6px 12px rgba(0,0,0,0.3)';
    container.style.maxWidth = '100%';
    container.style.boxSizing = 'border-box';

    // Title
    const title = document.createElement('h3');
    title.innerText = 'Photo To Music Track Converter';
    title.style.margin = '0';
    title.style.color = '#00ffcc';

    // Figure out optimal canvas dimensions for visualizer
    const maxW = 800;
    let vw = originalImg.naturalWidth || originalImg.width || 400;
    let vh = originalImg.naturalHeight || originalImg.height || 300;
    if (vw > maxW) {
        vh = Math.floor(vh * (maxW / vw));
        vw = maxW;
    }

    // Image & Playhead Wrapper
    const wrapper = document.createElement('div');
    wrapper.style.position = 'relative';
    wrapper.style.display = 'inline-block';
    wrapper.style.maxWidth = '100%';
    wrapper.style.borderRadius = '8px';
    wrapper.style.overflow = 'hidden';
    wrapper.style.lineHeight = '0';
    wrapper.style.boxShadow = '0 4px 10px rgba(0,0,0,0.5)';

    const visualizer = document.createElement('canvas');
    visualizer.width = vw;
    visualizer.height = vh;
    visualizer.style.display = 'block';
    visualizer.style.width = '100%';
    visualizer.style.height = 'auto';
    const vCtx = visualizer.getContext('2d');
    vCtx.drawImage(originalImg, 0, 0, vw, vh);

    const playhead = document.createElement('div');
    playhead.style.position = 'absolute';
    playhead.style.top = '0';
    playhead.style.left = '0';
    playhead.style.width = '3px';
    playhead.style.height = '100%';
    playhead.style.backgroundColor = 'rgba(0, 255, 204, 0.8)';
    playhead.style.boxShadow = '0 0 10px #00ffcc';
    playhead.style.pointerEvents = 'none';
    playhead.style.display = 'none';
    playhead.style.transition = 'left 0.1s linear';

    wrapper.appendChild(visualizer);
    wrapper.appendChild(playhead);

    // Status / Loading Indicator
    const status = document.createElement('div');
    status.innerText = 'Analyzing photo and synthesizing audio... 0%';
    status.style.fontSize = '1.1em';
    status.style.fontWeight = 'bold';
    status.style.color = '#ffcc00';

    container.appendChild(title);
    container.appendChild(wrapper);
    container.appendChild(status);

    // Kick off asynchronous rendering to not completely block the UI thread immediately
    setTimeout(async () => {
        try {
            // Resample image down to Time(W) vs Frequency(H) grid
            const gridCanvas = document.createElement('canvas');
            gridCanvas.width = W;
            gridCanvas.height = H;
            const ctx = gridCanvas.getContext('2d');
            ctx.fillStyle = '#000000'; // Remove alpha gaps
            ctx.fillRect(0, 0, W, H);
            ctx.drawImage(originalImg, 0, 0, W, H);
            const imgData = ctx.getImageData(0, 0, W, H).data;

            // Extract brightness amplitudes matrix
            const amplitudes = [];
            for (let y = 0; y < H; y++) {
                const row = [];
                for (let x = 0; x < W; x++) {
                    const idx = (y * W + x) * 4;
                    const r = imgData[idx];
                    const g = imgData[idx + 1];
                    const b = imgData[idx + 2];
                    // Non-linear brightness mappings for punchier isolated notes vs noise
                    const brightness = Math.pow((0.299 * r + 0.587 * g + 0.114 * b) / 255.0, 2); 
                    row.push(brightness);
                }
                amplitudes.push(row);
            }

            // Audio generation
            const sampleRate = 44100;
            const numSamples = Math.floor(duration * sampleRate);
            const audioData = new Float32Array(numSamples);

            for (let y = 0; y < H; y++) {
                // Pitch mapping: Logarithmic scale from bottom to top of image
                const freq = minFreq * Math.pow(maxFreq / minFreq, (H - 1 - y) / (H - 1));
                const A = amplitudes[y];
                let phase = 0;
                const phaseIncrement = 2 * Math.PI * freq / sampleRate;

                for (let i = 0; i < numSamples; i++) {
                    const target_x = (i / numSamples) * (W - 1);
                    const x_idx = Math.floor(target_x);
                    let amp = 0;
                    
                    // Linear interpolation of amplitude along the timeline
                    if (x_idx >= W - 1) {
                        amp = A[W - 1];
                    } else {
                        const x_frac = target_x - x_idx;
                        amp = A[x_idx] * (1 - x_frac) + A[x_idx + 1] * x_frac;
                    }

                    audioData[i] += amp * Math.sin(phase);
                    phase += phaseIncrement;
                }

                // Yield to visually update the status
                if (y % 5 === 0 || y === H - 1) {
                    status.innerText = `Synthesizing audio... ${Math.round(((y + 1) / H) * 100)}%`;
                    await new Promise(r => setTimeout(r, 0));
                }
            }

            status.innerText = 'Normalizing output...';
            await new Promise(r => setTimeout(r, 0));

            // Anti-clicking envelope at ends and finding max value to normalize
            const fadeSamples = Math.floor(0.05 * sampleRate); 
            let maxVal = 0;
            for (let i = 0; i < numSamples; i++) {
                let envelope = 1;
                if (i < fadeSamples) envelope = i / fadeSamples;
                else if (i > numSamples - fadeSamples) envelope = (numSamples - i) / fadeSamples;
                
                audioData[i] *= envelope;
                if (Math.abs(audioData[i]) > maxVal) maxVal = Math.abs(audioData[i]);
            }

            // Normalize audio signal to avoid distortion
            if (maxVal > 0) {
                for (let i = 0; i < numSamples; i++) {
                    audioData[i] = (audioData[i] / maxVal) * 0.9;
                }
            }

            status.innerText = 'Finalizing WAV file...';
            await new Promise(r => setTimeout(r, 0));

            // Construct valid WAV ArrayBuffer
            const arrayBuffer = new ArrayBuffer(44 + numSamples * 2);
            const view = new DataView(arrayBuffer);
            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 + numSamples * 2, true);
            writeString(view, 8, 'WAVE');
            writeString(view, 12, 'fmt ');
            view.setUint32(16, 16, true);
            view.setUint16(20, 1, true); 
            view.setUint16(22, 1, true); 
            view.setUint32(24, sampleRate, true); 
            view.setUint32(28, sampleRate * 2, true);
            view.setUint16(32, 2, true); 
            view.setUint16(34, 16, true); 
            writeString(view, 36, 'data');
            view.setUint32(40, numSamples * 2, true); 

            // Convert Float32 mapped track into 16-bit PCM Audio formatting
            let offset = 44;
            for (let i = 0; i < numSamples; i++) {
                let s = audioData[i];
                view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                offset += 2;
            }

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

            // Audio Player Controls structure
            const controlsDiv = document.createElement('div');
            controlsDiv.style.display = 'flex';
            controlsDiv.style.flexDirection = 'column';
            controlsDiv.style.alignItems = 'center';
            controlsDiv.style.width = '100%';
            controlsDiv.style.gap = '15px';

            const audioPlayer = document.createElement('audio');
            audioPlayer.controls = true;
            audioPlayer.src = url;
            audioPlayer.style.width = '100%';
            audioPlayer.style.maxWidth = '400px';

            // Connect playhead visual logic to the audio player
            audioPlayer.addEventListener('play', () => {
                playhead.style.display = 'block';
            });
            audioPlayer.addEventListener('timeupdate', () => {
                const progress = audioPlayer.currentTime / audioPlayer.duration;
                if (!isNaN(progress)) {
                    playhead.style.left = `${progress * 100}%`;
                }
            });
            audioPlayer.addEventListener('ended', () => {
                playhead.style.display = 'none';
                playhead.style.left = '0';
            });

            // Music track downloader
            const dlBtn = document.createElement('a');
            dlBtn.innerText = 'Download Music Track';
            dlBtn.download = 'Photo_Music_Track.wav';
            dlBtn.href = url;
            dlBtn.style.padding = '12px 24px';
            dlBtn.style.backgroundColor = '#00ffcc';
            dlBtn.style.color = '#1e1e24';
            dlBtn.style.textDecoration = 'none';
            dlBtn.style.borderRadius = '6px';
            dlBtn.style.fontWeight = 'bold';
            dlBtn.style.transition = 'opacity 0.2s';
            dlBtn.onmouseenter = () => dlBtn.style.opacity = '0.8';
            dlBtn.onmouseleave = () => dlBtn.style.opacity = '1';

            controlsDiv.appendChild(audioPlayer);
            controlsDiv.appendChild(dlBtn);
            
            // Swap out status text for interactive elements
            container.replaceChild(controlsDiv, status);

        } catch (error) {
            status.innerText = 'An error occurred during conversion.';
            status.style.color = '#ff4444';
            console.error('Audio generation failed:', error);
        }
    }, 50);

    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

This tool converts images into unique audio tracks by mapping visual data to sound frequencies and amplitudes. By analyzing the brightness and structure of a photo, the converter synthesizes a WAV audio file where different parts of the image correspond to specific pitches and volumes. This can be used for creative artistic projects, generating experimental ambient sounds from photography, or creating unique sonic interpretations of visual art.

Leave a Reply

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