You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, title = "Telephone Ringtone Mp3", artist = "Android Sound Player", lyrics = "🎵 Ring... Ring... Ring... 🎵", accentColor = "#3ddc84") {
// Create the main wrapper container representing the Android MP3 Player UI
const container = document.createElement('div');
container.style.position = 'relative';
container.style.width = '100%';
container.style.maxWidth = '800px';
container.style.aspectRatio = '2.55 / 1';
container.style.borderRadius = '16px';
container.style.overflow = 'hidden';
container.style.fontFamily = '"Roboto", "Segoe UI", sans-serif';
container.style.backgroundColor = '#121212';
container.style.boxShadow = '0 8px 24px rgba(0,0,0,0.5)';
container.style.margin = '0 auto';
// Set up canvas to crop image dynamically to exact 2.55:1 aspect ratio
const canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.objectFit = 'cover';
const targetRatio = 2.55;
// Set a high resolution for crisp rendering
canvas.width = 1000;
canvas.height = Math.round(1000 / targetRatio);
const ctx = canvas.getContext('2d');
const imgRatio = originalImg.width / originalImg.height;
// Calculate center-crop dimensions for 2.55:1
let sWidth, sHeight, sx, sy;
if (imgRatio > targetRatio) {
// Image is wider than 2.55:1
sHeight = originalImg.height;
sWidth = sHeight * targetRatio;
sx = (originalImg.width - sWidth) / 2;
sy = 0;
} else {
// Image is taller than 2.55:1
sWidth = originalImg.width;
sHeight = sWidth / targetRatio;
sx = 0;
sy = (originalImg.height - sHeight) / 2;
}
ctx.drawImage(originalImg, sx, sy, sWidth, sHeight, 0, 0, canvas.width, canvas.height);
container.appendChild(canvas);
// Overlay to ensure text readability (gradients)
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.inset = '0';
overlay.style.background = 'linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 30%, rgba(0,0,0,0.85) 100%)';
container.appendChild(overlay);
// Song Lyrics Element
const lyricEl = document.createElement('div');
lyricEl.style.position = 'absolute';
lyricEl.style.top = '15%';
lyricEl.style.left = '50%';
lyricEl.style.transform = 'translateX(-50%)';
lyricEl.style.color = '#fff';
lyricEl.style.fontSize = 'min(3vw, 16px)';
lyricEl.style.fontStyle = 'italic';
lyricEl.style.textShadow = '0px 2px 4px rgba(0,0,0,0.8)';
lyricEl.style.opacity = '0.9';
lyricEl.style.textAlign = 'center';
lyricEl.style.whiteSpace = 'nowrap';
lyricEl.innerText = lyrics;
container.appendChild(lyricEl);
// Bottom Player UI container (Android Style)
const playerUI = document.createElement('div');
playerUI.style.position = 'absolute';
playerUI.style.bottom = '15%';
playerUI.style.left = '50%';
playerUI.style.transform = 'translateX(-50%)';
playerUI.style.width = '90%';
playerUI.style.display = 'flex';
playerUI.style.flexDirection = 'column';
playerUI.style.gap = '10px';
container.appendChild(playerUI);
// Track Info & Time Row
const infoRow = document.createElement('div');
infoRow.style.display = 'flex';
infoRow.style.justifyContent = 'space-between';
infoRow.style.alignItems = 'flex-end';
const trackInfo = document.createElement('div');
const titleEl = document.createElement('div');
titleEl.textContent = title;
titleEl.style.color = '#fff';
titleEl.style.fontWeight = 'bold';
titleEl.style.fontSize = 'min(4vw, 20px)';
titleEl.style.textShadow = '0 1px 3px rgba(0,0,0,0.8)';
titleEl.style.marginBottom = '2px';
const artistEl = document.createElement('div');
artistEl.textContent = artist;
artistEl.style.color = '#e0e0e0';
artistEl.style.fontSize = 'min(2.5vw, 13px)';
trackInfo.appendChild(titleEl);
trackInfo.appendChild(artistEl);
infoRow.appendChild(trackInfo);
const timeEl = document.createElement('div');
timeEl.textContent = '0:00 / 0:30';
timeEl.style.color = '#e0e0e0';
timeEl.style.fontSize = 'min(2.5vw, 13px)';
timeEl.style.fontFamily = 'monospace';
infoRow.appendChild(timeEl);
playerUI.appendChild(infoRow);
// Controls & Progress Bar Row
const controlRow = document.createElement('div');
controlRow.style.display = 'flex';
controlRow.style.alignItems = 'center';
controlRow.style.gap = '16px';
const playBtn = document.createElement('button');
playBtn.innerHTML = '▶'; // Play icon
playBtn.style.width = 'min(10vw, 44px)';
playBtn.style.height = 'min(10vw, 44px)';
playBtn.style.borderRadius = '50%';
playBtn.style.border = 'none';
playBtn.style.backgroundColor = accentColor;
playBtn.style.color = '#000';
playBtn.style.fontSize = '18px';
playBtn.style.cursor = 'pointer';
playBtn.style.display = 'flex';
playBtn.style.justifyContent = 'center';
playBtn.style.alignItems = 'center';
playBtn.style.paddingLeft = '4px'; // Offset visual center for play icon
playBtn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.4)';
playBtn.style.transition = 'transform 0.1s ease';
playBtn.onmousedown = () => playBtn.style.transform = 'scale(0.95)';
playBtn.onmouseup = () => playBtn.style.transform = 'scale(1)';
playBtn.onmouseleave = () => playBtn.style.transform = 'scale(1)';
const progressContainer = document.createElement('div');
progressContainer.style.flexGrow = '1';
progressContainer.style.height = '4px';
progressContainer.style.backgroundColor = 'rgba(255,255,255,0.3)';
progressContainer.style.borderRadius = '2px';
progressContainer.style.position = 'relative';
const progressBar = document.createElement('div');
progressBar.style.width = '0%';
progressBar.style.height = '100%';
progressBar.style.backgroundColor = accentColor;
progressBar.style.borderRadius = '2px';
progressBar.style.transition = 'width 0.1s linear';
// Playhead dot
const playHead = document.createElement('div');
playHead.style.position = 'absolute';
playHead.style.right = '-6px';
playHead.style.top = '-4px';
playHead.style.width = '12px';
playHead.style.height = '12px';
playHead.style.backgroundColor = accentColor;
playHead.style.borderRadius = '50%';
playHead.style.boxShadow = '0 0 4px rgba(0,0,0,0.5)';
progressBar.appendChild(playHead);
progressContainer.appendChild(progressBar);
controlRow.appendChild(playBtn);
controlRow.appendChild(progressContainer);
playerUI.appendChild(controlRow);
// --- Audio Rendering Logic via Web Audio API (Synthesizing a Phone Ringtone) ---
let audioCtx, osc1, osc2, gainNode;
let playing = false;
let ringInterval, timeInterval, lyricAnimation;
let currentSeconds = 0;
const totalDuration = 30; // Max allowed mp3 playback duration
function formatTime(s) {
const min = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return `${min}:${sec < 10 ? '0' : ''}${sec}`;
}
playBtn.addEventListener('click', () => {
// Initialize Audio context securely on user gesture
if (!audioCtx) {
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
// Synthesize standard UK/European telephone ring frequency (400Hz + 450Hz sine waves)
osc1 = audioCtx.createOscillator();
osc1.type = 'sine';
osc1.frequency.value = 400;
osc2 = audioCtx.createOscillator();
osc2.type = 'sine';
osc2.frequency.value = 450;
gainNode = audioCtx.createGain();
gainNode.gain.value = 0;
const compressor = audioCtx.createDynamicsCompressor();
osc1.connect(gainNode);
osc2.connect(gainNode);
gainNode.connect(compressor);
compressor.connect(audioCtx.destination);
osc1.start();
osc2.start();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
if (playing) {
// PAUSE
playing = false;
playBtn.innerHTML = '▶';
playBtn.style.paddingLeft = '4px';
clearInterval(ringInterval);
clearInterval(timeInterval);
clearInterval(lyricAnimation);
lyricEl.style.opacity = '0.9';
gainNode.gain.setTargetAtTime(0, audioCtx.currentTime, 0.05);
} else {
// PLAY
playing = true;
playBtn.innerHTML = '⏸';
playBtn.style.paddingLeft = '0px';
// Ringtone generation (Cadence: double ring, then silence)
let step = 0;
const volumeMax = 0.4;
ringInterval = setInterval(() => {
step = (step + 1) % 30; // 3 sec loop (30 steps of 100ms)
// On: 0-4, Off: 4-6, On: 6-10, Off: 10-30
if (step < 4 || (step >= 6 && step < 10)) {
gainNode.gain.setTargetAtTime(volumeMax, audioCtx.currentTime, 0.05);
} else {
gainNode.gain.setTargetAtTime(0, audioCtx.currentTime, 0.05);
}
}, 100);
// Progress bar and time handling
timeInterval = setInterval(() => {
currentSeconds += 0.1;
if (currentSeconds >= totalDuration) {
currentSeconds = 0;
}
progressBar.style.width = (currentSeconds / totalDuration * 100) + '%';
timeEl.textContent = `${formatTime(currentSeconds)} / ${formatTime(totalDuration)}`;
}, 100);
// Pulse lyrics text visually while playing
lyricAnimation = setInterval(() => {
const fade = (Math.sin(Date.now() / 250) * 0.3) + 0.7;
lyricEl.style.opacity = fade.toString();
}, 50);
}
});
return container;
}
Apply Changes