You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, defaultDuration = "30") {
// Create the main UI container for the tool
const container = document.createElement('div');
container.style.width = '100%';
container.style.maxWidth = '450px';
container.style.margin = '0 auto';
container.style.padding = '30px 20px';
container.style.boxSizing = 'border-box';
container.style.position = 'relative';
container.style.borderRadius = '20px';
container.style.boxShadow = '0 15px 35px rgba(0,0,0,0.5)';
container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
container.style.color = '#fff';
container.style.overflow = 'hidden';
container.style.textAlign = 'center';
container.style.background = '#222';
// Set the provided image as a blurred artistic background
const bg = document.createElement('div');
bg.style.position = 'absolute';
bg.style.top = '-20px';
bg.style.left = '-20px';
bg.style.width = 'calc(100% + 40px)';
bg.style.height = 'calc(100% + 40px)';
bg.style.backgroundImage = `url("${originalImg.src}")`;
bg.style.backgroundSize = 'cover';
bg.style.backgroundPosition = 'center';
bg.style.filter = 'blur(15px) brightness(35%)';
bg.style.zIndex = '0';
container.appendChild(bg);
// Content wrapper to sit above the blurred background
const content = document.createElement('div');
content.style.position = 'relative';
content.style.zIndex = '1';
container.appendChild(content);
// Display original image as a "cover art" for the ringtone
const cover = document.createElement('img');
cover.src = originalImg.src;
cover.style.width = '200px';
cover.style.height = '200px';
cover.style.objectFit = 'cover';
cover.style.borderRadius = '15px';
cover.style.boxShadow = '0 10px 20px rgba(0,0,0,0.6)';
cover.style.marginBottom = '25px';
content.appendChild(cover);
const title = document.createElement('h2');
title.innerText = 'MP3 to Ringtone Converter';
title.style.margin = '0 0 10px 0';
title.style.fontSize = '22px';
title.style.fontWeight = 'bold';
content.appendChild(title);
const subtitle = document.createElement('p');
subtitle.innerText = 'Select an MP3 file to trim and convert to a mobile phone ringtone format.';
subtitle.style.margin = '0 0 20px 0';
subtitle.style.fontSize = '14px';
subtitle.style.color = '#ddd';
content.appendChild(subtitle);
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'audio/mp3, audio/mpeg';
fileInput.style.display = 'block';
fileInput.style.width = '100%';
fileInput.style.margin = '0 auto 20px auto';
fileInput.style.padding = '10px';
fileInput.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
fileInput.style.borderRadius = '10px';
fileInput.style.border = '1px solid rgba(255, 255, 255, 0.2)';
content.appendChild(fileInput);
const controls = document.createElement('div');
controls.style.display = 'flex';
controls.style.justifyContent = 'center';
controls.style.gap = '20px';
controls.style.marginBottom = '25px';
const startLabel = document.createElement('label');
startLabel.innerText = 'Start time (sec)\n';
startLabel.style.fontSize = '13px';
startLabel.style.fontWeight = 'bold';
const startInput = document.createElement('input');
startInput.type = 'number';
startInput.value = '0';
startInput.min = '0';
startInput.style.width = '100%';
startInput.style.marginTop = '5px';
startInput.style.padding = '8px';
startInput.style.borderRadius = '5px';
startInput.style.border = 'none';
startInput.style.textAlign = 'center';
startLabel.appendChild(startInput);
const durLabel = document.createElement('label');
durLabel.innerText = 'Duration (sec)\n';
durLabel.style.fontSize = '13px';
durLabel.style.fontWeight = 'bold';
const durInput = document.createElement('input');
durInput.type = 'number';
durInput.value = defaultDuration;
durInput.min = '1';
durInput.max = '60';
durInput.style.width = '100%';
durInput.style.marginTop = '5px';
durInput.style.padding = '8px';
durInput.style.borderRadius = '5px';
durInput.style.border = 'none';
durInput.style.textAlign = 'center';
durLabel.appendChild(durInput);
controls.appendChild(startLabel);
controls.appendChild(durLabel);
content.appendChild(controls);
const btn = document.createElement('button');
btn.innerText = 'Generate Phone Ringtone';
btn.style.width = '100%';
btn.style.padding = '15px';
btn.style.backgroundColor = '#1DB954'; // Spotify green feel
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '30px';
btn.style.cursor = 'pointer';
btn.style.fontSize = '16px';
btn.style.fontWeight = 'bold';
btn.style.boxShadow = '0 5px 15px rgba(29, 185, 84, 0.4)';
btn.style.transition = 'transform 0.1s ease';
btn.onmousedown = () => btn.style.transform = 'scale(0.97)';
btn.onmouseup = () => btn.style.transform = 'scale(1)';
content.appendChild(btn);
const status = document.createElement('p');
status.style.fontSize = '14px';
status.style.marginTop = '15px';
status.style.fontWeight = 'bold';
status.style.minHeight = '20px';
content.appendChild(status);
const previewAudio = document.createElement('audio');
previewAudio.controls = true;
previewAudio.style.marginTop = '15px';
previewAudio.style.display = 'none';
previewAudio.style.width = '100%';
previewAudio.style.borderRadius = '30px';
content.appendChild(previewAudio);
// Audio Processing Logic
btn.addEventListener('click', async () => {
if (!fileInput.files.length) {
status.innerText = 'Please select an MP3 file first.';
status.style.color = '#ff4d4d';
return;
}
status.innerText = 'Decoding & Processing audio...';
status.style.color = '#ffcc00';
btn.disabled = true;
btn.style.opacity = '0.5';
previewAudio.style.display = 'none';
try {
const file = fileInput.files[0];
const arrayBuffer = await file.arrayBuffer();
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
const start = parseFloat(startInput.value) || 0;
let duration = parseFloat(durInput.value) || 30;
if (start + duration > audioBuffer.duration) {
duration = audioBuffer.duration - start;
}
if (duration <= 0 || start < 0 || start >= audioBuffer.duration) {
status.innerText = 'Invalid start time or duration.';
status.style.color = '#ff4d4d';
btn.disabled = false;
btn.style.opacity = '1';
return;
}
// Create OfflineAudioContext to render the trimmed audio segment
const offlineCtx = new OfflineAudioContext(
audioBuffer.numberOfChannels,
Math.ceil(audioCtx.sampleRate * duration),
audioCtx.sampleRate
);
const source = offlineCtx.createBufferSource();
source.buffer = audioBuffer;
source.connect(offlineCtx.destination);
source.start(0, start, duration);
const renderedBuffer = await offlineCtx.startRendering();
// Convert Processed AudioBuffer to Standard WAV ringtone
const wavData = audioBufferToWav(renderedBuffer);
const blob = new Blob([new DataView(wavData)], { type: 'audio/wav' });
const url = URL.createObjectURL(blob);
// Allow preview
previewAudio.src = url;
previewAudio.style.display = 'block';
// Automatic Download
const a = document.createElement('a');
a.href = url;
a.download = `ringtone_${Date.now()}.wav`; // Saved as WAV compatible with majority of modern mobile software
a.click();
status.innerText = 'Done! Download triggered automatically.';
status.style.color = '#00e676';
} catch (e) {
console.error(e);
status.innerText = 'Error occurred during processing.';
status.style.color = '#ff4d4d';
}
btn.disabled = false;
btn.style.opacity = '1';
});
// Helper utility internally converting the AudioBuffer PCM chunk to WAV headers and interleaved Data chunk
function audioBufferToWav(buffer) {
const numChannels = buffer.numberOfChannels;
const sampleRate = buffer.sampleRate;
const format = 1; // PCM
const bitDepth = 16;
let pcmLength = buffer.length * numChannels * (bitDepth / 8);
let arrayBuffer = new ArrayBuffer(44 + pcmLength);
let view = new DataView(arrayBuffer);
// Helper string writer for headers
const writeString = (v, offset, string) => {
for (let i = 0; i < string.length; i++) {
v.setUint8(offset + i, string.charCodeAt(i));
}
};
/* Add standard RIFF generic format container rules */
writeString(view, 0, 'RIFF');
view.setUint32(4, 36 + pcmLength, true);
writeString(view, 8, 'WAVE');
/* Format Sub-chunk */
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, format, true);
view.setUint16(22, numChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * numChannels * (bitDepth / 8), true);
view.setUint16(32, numChannels * (bitDepth / 8), true);
view.setUint16(34, bitDepth, true);
/* Data Sub-chunk */
writeString(view, 36, 'data');
view.setUint32(40, pcmLength, true);
/* Interleaved recording variables onto arrayBuffer */
let offset = 44;
let channelsData = [];
for (let i = 0; i < numChannels; i++) {
channelsData.push(buffer.getChannelData(i));
}
for (let i = 0; i < buffer.length; i++) {
for (let channel = 0; channel < numChannels; channel++) {
let sample = channelsData[channel][i];
// clamp and map into a standard 16 bit PCM threshold
sample = Math.max(-1, Math.min(1, sample));
sample = sample < 0 ? sample * 32768 : sample * 32767;
view.setInt16(offset, sample, true);
offset += 2;
}
}
return arrayBuffer;
}
return container;
}
Apply Changes