Please bookmark this page to avoid losing your image tool!

Image Melody Finder

(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, scaleType = 'major', rootNoteName = 'C4', noteDuration = 0.15, scanResolution = 128, instrument = 'sine') {

    /**
     * Converts a musical note name (e.g., 'A4', 'C#5') to its MIDI number.
     * @param {string} note - The note name.
     * @returns {number} The MIDI note number, or a default if invalid.
     */
    const noteToMidi = (note) => {
        const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
        const match = note.match(/([A-G]#?)([0-9])/);
        if (!match) return 60; // Default to C4 if format is wrong
        
        const name = match[1];
        const octave = parseInt(match[2], 10);
        const noteIndex = notes.indexOf(name);

        if (noteIndex < 0) return 60; // Default to C4 if note name is invalid
        
        // MIDI note number formula
        return 12 + octave * 12 + noteIndex;
    };
    
    /**
     * Converts a MIDI note number to a frequency in Hz.
     * @param {number} midi - The MIDI note number.
     * @returns {number} The frequency in Hertz.
     */
    const midiToFreq = (midi) => 440 * Math.pow(2, (midi - 69) / 12);

    /**
     * Generates an array of frequencies for a given musical scale.
     * @param {string} rootNote - The starting note, e.g., 'C4'.
     * @param {string} scale - The type of scale ('major', 'minor', 'pentatonic').
     * @param {number} octaves - The number of octaves to generate.
     * @returns {number[]} An array of frequencies.
     */
    const getScaleFrequencies = (rootNote, scale, octaves = 3) => {
        const scales = {
            major:      [0, 2, 4, 5, 7, 9, 11],
            minor:      [0, 2, 3, 5, 7, 8, 10],
            pentatonic: [0, 2, 4, 7, 9],
        };
        const intervals = scales[scale] || scales.major;
        const startMidi = noteToMidi(rootNote);

        const frequencies = [];
        for (let o = 0; o < octaves; o++) {
            for (const interval of intervals) {
                frequencies.push(midiToFreq(startMidi + o * 12 + interval));
            }
        }
        // Add the final root note of the next octave to complete the scale range
        frequencies.push(midiToFreq(startMidi + octaves * 12));
        
        return frequencies;
    };

    // --- Main Logic ---

    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.gap = '10px';
    container.style.fontFamily = 'sans-serif';

    const vizCanvas = document.createElement('canvas');
    const vizCtx = vizCanvas.getContext('2d');
    const aspectRatio = originalImg.width / originalImg.height;
    const maxWidth = 500;
    vizCanvas.width = Math.min(originalImg.width, maxWidth);
    vizCanvas.height = vizCanvas.width / aspectRatio;
    vizCtx.drawImage(originalImg, 0, 0, vizCanvas.width, vizCanvas.height);

    const analysisCanvas = document.createElement('canvas');
    const analysisCtx = analysisCanvas.getContext('2d', { willReadFrequently: true });
    analysisCanvas.width = scanResolution;
    analysisCanvas.height = Math.round(scanResolution / aspectRatio) || 1;
    analysisCtx.drawImage(originalImg, 0, 0, analysisCanvas.width, analysisCanvas.height);
    const imageData = analysisCtx.getImageData(0, 0, analysisCanvas.width, analysisCanvas.height).data;

    const melody = [];
    const noteMarkers = [];
    const noteFrequencies = getScaleFrequencies(rootNoteName, scaleType).reverse(); // Reverse for high-pitch at top

    for (let x = 0; x < analysisCanvas.width; x++) {
        let darkestPix = { y: -1, brightness: 256 };
        // Find the darkest pixel in the current column
        for (let y = 0; y < analysisCanvas.height; y++) {
            const i = (y * analysisCanvas.width + x) * 4;
            const r = imageData[i];
            const g = imageData[i + 1];
            const b = imageData[i + 2];
            const brightness = (r * 0.299 + g * 0.587 + b * 0.114); // Perceived brightness formula

            if (brightness < darkestPix.brightness) {
                darkestPix = { y, brightness };
            }
        }

        if (darkestPix.y !== -1) {
            const normalizedY = (analysisCanvas.height === 1) ? 0.5 : darkestPix.y / (analysisCanvas.height - 1);
            const noteIndex = Math.min(noteFrequencies.length - 1, Math.floor(normalizedY * noteFrequencies.length));
            const frequency = noteFrequencies[noteIndex];
            const volume = 0.4 * (1 - (darkestPix.brightness / 255)); // Darker = louder

            if (frequency) {
                 melody.push({
                    freq: frequency,
                    vol: volume,
                    time: x * noteDuration,
                });
                
                noteMarkers.push({
                    x: (x / (analysisCanvas.width - 1)) * vizCanvas.width,
                    y: normalizedY * vizCanvas.height
                });
            }
        }
    }
    
    noteMarkers.forEach(marker => {
        vizCtx.beginPath();
        vizCtx.arc(marker.x, marker.y, 3, 0, 2 * Math.PI, false);
        vizCtx.fillStyle = 'rgba(255, 0, 80, 0.8)';
        vizCtx.fill();
        vizCtx.lineWidth = 1.5;
        vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.9)';
        vizCtx.stroke();
    });

    const playButton = document.createElement('button');
    playButton.textContent = '▶️ Play Melody';
    playButton.style.padding = '10px 20px';
    playButton.style.fontSize = '16px';
    playButton.style.cursor = 'pointer';
    playButton.style.border = '1px solid #ccc';
    playButton.style.borderRadius = '5px';

    if (melody.length === 0) {
        playButton.disabled = true;
        playButton.textContent = 'No melody found in image';
    }
    
    let audioContext; 

    playButton.onclick = () => {
        if (!audioContext || audioContext.state === 'closed') {
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
        } else if (audioContext.state === 'suspended') {
            audioContext.resume();
        }

        if (audioContext.state !== 'running') return;

        playButton.disabled = true;
        playButton.textContent = 'Playing...';
        const startTime = audioContext.currentTime;
        const attackTime = 0.01;
        const releaseTime = noteDuration * 0.9;

        melody.forEach(note => {
            const oscillator = audioContext.createOscillator();
            const gainNode = audioContext.createGain();

            oscillator.type = instrument;
            oscillator.frequency.value = note.freq;

            gainNode.connect(audioContext.destination);
            oscillator.connect(gainNode);

            // Simple attack/release envelope for each note
            gainNode.gain.setValueAtTime(0, startTime + note.time);
            gainNode.gain.linearRampToValueAtTime(note.vol, startTime + note.time + attackTime);
            gainNode.gain.setValueAtTime(note.vol, startTime + note.time + noteDuration - releaseTime);
            gainNode.gain.linearRampToValueAtTime(0, startTime + note.time + noteDuration);

            oscillator.start(startTime + note.time);
            oscillator.stop(startTime + note.time + noteDuration + 0.01);
        });

        const totalDurationMs = (scanResolution * noteDuration * 1000) + 100;
        setTimeout(() => {
            playButton.disabled = false;
            playButton.textContent = '▶️ Play Melody';
        }, totalDurationMs);
    };

    container.appendChild(vizCanvas);
    container.appendChild(playButton);

    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

Image Melody Finder is a tool that converts visual images into musical melodies. By analyzing the brightness of pixels in an image, it generates a melody based on specified musical scales, providing a unique auditory interpretation of the visual content. Users can input an image and customize parameters such as the musical scale, root note, and note duration. This tool is ideal for musicians, artists, or anyone interested in exploring the intersection of visual art and music, enabling creative exploration and experimentation with sound derived from images.

Leave a Reply

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