Please bookmark this page to avoid losing your image tool!

Mp3 File To Phone Ringtone Creator

(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, ringtoneTitle = "My Ringtone", durationSec = 30) {
    durationSec = Number(durationSec);

    // Create main container
    const container = document.createElement('div');
    container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
    container.style.padding = '24px';
    container.style.maxWidth = '360px';
    container.style.background = '#ffffff';
    container.style.border = '1px solid #e0e0e0';
    container.style.borderRadius = '16px';
    container.style.boxShadow = '0 8px 24px rgba(0,0,0,0.1)';
    container.style.textAlign = 'center';
    container.style.margin = '0 auto';
    container.style.color = '#333';

    // Header
    const header = document.createElement('h2');
    header.textContent = 'MP3 to Phone Ringtone';
    header.style.marginTop = '0';
    header.style.marginBottom = '20px';
    header.style.fontSize = '20px';
    container.appendChild(header);

    // Process originalImg as album art / cover
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const size = 160;
    canvas.width = size;
    canvas.height = size;
    canvas.style.borderRadius = '12px';
    canvas.style.objectFit = 'cover';
    canvas.style.boxShadow = '0 6px 16px rgba(0,0,0,0.15)';
    canvas.style.marginBottom = '20px';

    // Draw cover (crop to center square)
    const dim = Math.min(originalImg.width, originalImg.height);
    const x = (originalImg.width - dim) / 2;
    const y = (originalImg.height - dim) / 2;
    if (dim > 0) {
        ctx.drawImage(originalImg, x, y, dim, dim, 0, 0, size, size);
    }
    container.appendChild(canvas);

    // Title label
    const titleEl = document.createElement('h3');
    titleEl.textContent = ringtoneTitle;
    titleEl.style.marginTop = '0';
    titleEl.style.marginBottom = '20px';
    titleEl.style.fontSize = '18px';
    titleEl.style.fontWeight = '500';
    container.appendChild(titleEl);

    // File Upload Area
    const fileUploadLabel = document.createElement('label');
    fileUploadLabel.style.display = 'inline-block';
    fileUploadLabel.style.background = '#28a745';
    fileUploadLabel.style.color = '#fff';
    fileUploadLabel.style.padding = '10px 20px';
    fileUploadLabel.style.borderRadius = '8px';
    fileUploadLabel.style.cursor = 'pointer';
    fileUploadLabel.style.fontWeight = 'bold';
    fileUploadLabel.style.marginBottom = '16px';
    fileUploadLabel.style.transition = 'background 0.2s';
    fileUploadLabel.onmouseover = () => fileUploadLabel.style.background = '#218838';
    fileUploadLabel.onmouseout = () => fileUploadLabel.style.background = '#28a745';

    const fileUploadText = document.createElement('span');
    fileUploadText.textContent = 'Select MP3 / Audio File';
    fileUploadLabel.appendChild(fileUploadText);

    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = 'audio/mpeg, audio/mp3, audio/wav, audio/ogg';
    fileInput.style.display = 'none';
    fileUploadLabel.appendChild(fileInput);
    container.appendChild(fileUploadLabel);

    // Trimming & Playback Controls Container
    const controlsDiv = document.createElement('div');
    controlsDiv.style.display = 'none';
    controlsDiv.style.textAlign = 'left';
    
    const durLabel = document.createElement('label');
    durLabel.textContent = `Select part (max ${durationSec}s):`;
    durLabel.style.display = 'block';
    durLabel.style.marginBottom = '8px';
    durLabel.style.fontSize = '14px';
    durLabel.style.fontWeight = 'bold';

    const waveCanvas = document.createElement('canvas');
    waveCanvas.width = 312;
    waveCanvas.height = 60;
    waveCanvas.style.width = '100%';
    waveCanvas.style.height = '60px';
    waveCanvas.style.marginBottom = '10px';
    waveCanvas.style.borderRadius = '6px';
    waveCanvas.style.background = '#f8f9fa';
    waveCanvas.style.border = '1px solid #dee2e6';

    const startSlider = document.createElement('input');
    startSlider.type = 'range';
    startSlider.min = 0;
    startSlider.value = 0;
    startSlider.step = '0.1';
    startSlider.style.width = '100%';
    startSlider.style.marginBottom = '8px';

    const timeDisplay = document.createElement('div');
    timeDisplay.textContent = '0.0s - 0.0s';
    timeDisplay.style.textAlign = 'center';
    timeDisplay.style.marginBottom = '16px';
    timeDisplay.style.fontSize = '14px';
    timeDisplay.style.color = '#555';

    controlsDiv.appendChild(durLabel);
    controlsDiv.appendChild(waveCanvas);
    controlsDiv.appendChild(startSlider);
    controlsDiv.appendChild(timeDisplay);

    const btnGroup = document.createElement('div');
    btnGroup.style.display = 'flex';
    btnGroup.style.justifyContent = 'space-between';
    btnGroup.style.gap = '10px';

    const previewBtn = document.createElement('button');
    previewBtn.textContent = 'Preview';
    const downloadBtn = document.createElement('button');
    downloadBtn.textContent = 'Download Ringtone';

    [previewBtn, downloadBtn].forEach(btn => {
        btn.style.flex = '1';
        btn.style.padding = '10px';
        btn.style.border = 'none';
        btn.style.borderRadius = '8px';
        btn.style.background = '#007bff';
        btn.style.color = '#fff';
        btn.style.cursor = 'pointer';
        btn.style.fontWeight = 'bold';
        btn.style.transition = 'background 0.2s';
    });

    previewBtn.onmouseover = () => { if(previewBtn.textContent === 'Preview') previewBtn.style.background = '#0069d9'; };
    previewBtn.onmouseout = () => { if(previewBtn.textContent === 'Preview') previewBtn.style.background = '#007bff'; };
    downloadBtn.onmouseover = () => downloadBtn.style.background = '#0069d9';
    downloadBtn.onmouseout = () => downloadBtn.style.background = '#007bff';

    btnGroup.appendChild(previewBtn);
    btnGroup.appendChild(downloadBtn);
    controlsDiv.appendChild(btnGroup);
    container.appendChild(controlsDiv);

    // Audio & State Handling
    let audioCtx;
    let audioBuffer = null;
    let previewSource = null;
    let wavePeaks = [];

    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (!file) return;

        fileUploadText.textContent = 'Loading Audio...';
        
        const reader = new FileReader();
        reader.onload = async (ev) => {
            const arrayBuffer = ev.target.result;
            try {
                if (!audioCtx) {
                    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                }
                if (audioCtx.state === 'suspended') {
                    await audioCtx.resume();
                }
                audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
                
                fileUploadText.textContent = 'Change Audio File';
                
                // Pre-compute waveform peaks for performance
                wavePeaks = [];
                const data = audioBuffer.getChannelData(0);
                const step = Math.ceil(data.length / waveCanvas.width);
                for (let i = 0; i < waveCanvas.width; i++) {
                    let min = 1.0;
                    let max = -1.0;
                    const limit = Math.min((i + 1) * step, data.length);
                    for (let j = i * step; j < limit; j++) {
                        const datum = data[j];
                        if (datum < min) min = datum;
                        if (datum > max) max = datum;
                    }
                    wavePeaks.push({ min, max });
                }

                controlsDiv.style.display = 'block';
                const maxStart = Math.max(0, audioBuffer.duration - durationSec);
                startSlider.max = maxStart;
                startSlider.value = 0;
                
                updateTimeDisplay();
            } catch (err) {
                fileUploadText.textContent = 'Select MP3 / Audio File';
                alert("Error decoding audio file. Make sure it's a valid audio file format.");
                console.error(err);
            }
        };
        reader.readAsArrayBuffer(file);
    });

    startSlider.addEventListener('input', updateTimeDisplay);

    function updateTimeDisplay() {
        if (!audioBuffer) return;
        const start = parseFloat(startSlider.value);
        const end = Math.min(audioBuffer.duration, start + durationSec);
        timeDisplay.textContent = `${start.toFixed(1)}s - ${end.toFixed(1)}s`;
        
        // Draw Waveform and Playhead
        const wctx = waveCanvas.getContext('2d');
        const width = waveCanvas.width;
        const height = waveCanvas.height;
        wctx.clearRect(0, 0, width, height);

        const amp = height / 2;
        wctx.fillStyle = '#b3d4ff';
        for (let i = 0; i < width; i++) {
            if(!wavePeaks[i]) continue;
            const { min, max } = wavePeaks[i];
            wctx.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));
        }

        const startX = (start / audioBuffer.duration) * width;
        const endX = ((start + Math.min(audioBuffer.duration, durationSec)) / audioBuffer.duration) * width;

        wctx.fillStyle = 'rgba(0, 123, 255, 0.4)';
        wctx.fillRect(startX, 0, endX - startX, height);
        
        wctx.fillStyle = '#0056b3';
        wctx.fillRect(startX, 0, 2, height);
        wctx.fillRect(endX - 2, 0, 2, height);
    }

    previewBtn.addEventListener('click', async () => {
        if (!audioBuffer) return;
        if (audioCtx.state === 'suspended') {
             await audioCtx.resume();
        }
        if (previewSource) {
            previewSource.stop();
            previewBtn.textContent = 'Preview';
            previewBtn.style.background = '#007bff';
            previewSource = null;
            return;
        }
        
        previewSource = audioCtx.createBufferSource();
        previewSource.buffer = audioBuffer;
        previewSource.connect(audioCtx.destination);
        
        const start = parseFloat(startSlider.value);
        const playDur = Math.min(audioBuffer.duration - start, durationSec);
        
        previewSource.start(0, start, playDur);
        
        previewBtn.textContent = 'Stop';
        previewBtn.style.background = '#dc3545';
        
        previewSource.onended = () => {
            previewBtn.textContent = 'Preview';
            previewBtn.style.background = '#007bff';
            previewSource = null;
        };
    });

    downloadBtn.addEventListener('click', async () => {
        if (!audioBuffer) return;
        downloadBtn.textContent = 'Processing...';
        downloadBtn.disabled = true;

        setTimeout(async () => {
            try {
                const start = parseFloat(startSlider.value);
                const dur = Math.min(audioBuffer.duration - start, durationSec);

                // Use OfflineAudioContext for safe rendering length
                const OfflineCtxClass = window.OfflineAudioContext || window.webkitOfflineAudioContext;
                const lengthInSamples = Math.max(1, Math.ceil(audioBuffer.sampleRate * dur));
                
                const offlineCtx = new OfflineCtxClass(
                    audioBuffer.numberOfChannels,
                    lengthInSamples,
                    audioBuffer.sampleRate
                );

                const source = offlineCtx.createBufferSource();
                source.buffer = audioBuffer;
                source.connect(offlineCtx.destination);
                source.start(0, start, dur);

                const renderedBuffer = await offlineCtx.startRendering();
                const wavBlob = audioBufferToWav(renderedBuffer);
                const url = URL.createObjectURL(wavBlob);

                const a = document.createElement('a');
                a.href = url;
                
                // Keep localized title chars like cyrillic and latin
                const fileName = ringtoneTitle.replace(/[^\wа-яА-ЯёЁ\s-]/g, '_').trim() || 'ringtone';
                a.download = `${fileName}.wav`;
                
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
            } catch (e) {
                console.error(e);
                alert("Error exporting ringtone.");
            } finally {
                downloadBtn.textContent = 'Download Ringtone';
                downloadBtn.disabled = false;
            }
        }, 50);
    });

    // Helper Function: Convert AudioBuffer to WAV format Native handling
    function audioBufferToWav(buffer) {
        const numOfChan = buffer.numberOfChannels;
        const length = buffer.length * numOfChan * 2 + 44;
        const bufferArray = new ArrayBuffer(length);
        const view = new DataView(bufferArray);
        const channels = [];
        let i, sample, offset = 0, pos = 0;

        function setUint16(data) { view.setUint16(pos, data, true); pos += 2; }
        function setUint32(data) { view.setUint32(pos, data, true); pos += 4; }
        function setString(data) {
            for (let j = 0; j < data.length; j++) {
                view.setUint8(pos++, data.charCodeAt(j));
            }
        }

        // WAV Header writer
        setString('RIFF');
        setUint32(length - 8);
        setString('WAVE');
        setString('fmt ');
        setUint32(16);
        setUint16(1); // PCM 
        setUint16(numOfChan);
        setUint32(buffer.sampleRate);
        setUint32(buffer.sampleRate * 2 * numOfChan); // byte rate
        setUint16(numOfChan * 2); // block-align
        setUint16(16); // 16-bit
        setString('data');
        setUint32(length - pos - 4);

        for (i = 0; i < buffer.numberOfChannels; i++) {
            channels.push(buffer.getChannelData(i));
        }

        // Interleave & Write PCM Data
        while (pos < length) {
            for (i = 0; i < numOfChan; i++) {
                sample = Math.max(-1, Math.min(1, channels[i][offset])); // Hard clamp
                sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // -> 16 bit
                view.setInt16(pos, sample, true);
                pos += 2;
            }
            offset++;
        }

        return new Blob([bufferArray], { type: "audio/wav" });
    }

    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 Mp3 File To Phone Ringtone Creator allows you to transform your favorite audio files into custom ringtones. Users can upload MP3, WAV, OGG, or other audio formats, visualize the waveform, and select a specific segment of the track to keep. The tool provides a preview function to listen to your selection before downloading the final clip as a high-quality WAV file, making it ideal for personalizing mobile devices with specific song highlights or sound effects.

Leave a Reply

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