You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, audioUrl = "", title = "Mp3 Audio HIFI", artist = "Audio To Image Converter", themeColor = "#1db954") {
const width = 800;
const height = 1200;
// Create output canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Helper for perfectly rounded rectangles
function drawRoundRect(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
// 1. Draw Player Background (Blurred original cover + dark gradient overlay)
const bgScale = Math.max(width / originalImg.width, height / originalImg.height);
const bgSw = width / bgScale;
const bgSh = height / bgScale;
const bgSx = (originalImg.width - bgSw) / 2;
const bgSy = (originalImg.height - bgSh) / 2;
if (typeof ctx.filter !== 'undefined') {
ctx.save();
ctx.filter = 'blur(60px) brightness(0.4)';
// Draw slightly larger to avoid un-blurred edges
ctx.drawImage(originalImg, bgSx, bgSy, bgSw, bgSh, -50, -50, width + 100, height + 100);
ctx.restore();
} else {
// Fallback for setups without CSS filter support
const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
bgGradient.addColorStop(0, '#1a1a2e');
bgGradient.addColorStop(1, '#08080f');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, width, height);
}
// Overlay to ensure contrast
const overlayGradient = ctx.createLinearGradient(0, 0, 0, height);
overlayGradient.addColorStop(0, 'rgba(0, 0, 0, 0.4)');
overlayGradient.addColorStop(1, 'rgba(15, 15, 20, 0.9)');
ctx.fillStyle = overlayGradient;
ctx.fillRect(0, 0, width, height);
// 2. Draw Top HIFI Tag
const badgeW = 200;
const badgeH = 46;
const badgeX = (width - badgeW) / 2;
const badgeY = 50;
ctx.strokeStyle = '#ffd700'; // Hi-Res gold
ctx.lineWidth = 2;
drawRoundRect(ctx, badgeX, badgeY, badgeW, badgeH, 23);
ctx.stroke();
ctx.fillStyle = '#ffd700';
ctx.font = 'bold 20px "Trebuchet MS", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hi-Res AUDIO', width / 2, badgeY + badgeH / 2 + 2);
// 3. Draw Album Art (Original Image)
const artSize = 640;
const artX = (width - artSize) / 2;
const artY = 140;
const scale = Math.max(artSize / originalImg.width, artSize / originalImg.height);
const sw = artSize / scale;
const sh = artSize / scale;
const sx = (originalImg.width - sw) / 2;
const sy = (originalImg.height - sh) / 2;
// Album shadow
ctx.save();
drawRoundRect(ctx, artX, artY, artSize, artSize, 30);
ctx.shadowColor = 'rgba(0, 0, 0, 0.7)';
ctx.shadowBlur = 40;
ctx.shadowOffsetY = 20;
ctx.fill();
ctx.restore();
// Album cover clipping
ctx.save();
drawRoundRect(ctx, artX, artY, artSize, artSize, 30);
ctx.clip();
ctx.drawImage(originalImg, sx, sy, sw, sh, artX, artY, artSize, artSize);
ctx.restore();
// 4. Draw Track Info
ctx.fillStyle = '#ffffff';
let fontSize = 46;
ctx.font = `bold ${fontSize}px "Segoe UI", sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'alphabetic';
// Scale down text if too wide
const maxTextW = artSize - 40;
while(ctx.measureText(title).width > maxTextW && fontSize > 24) {
fontSize -= 2;
ctx.font = `bold ${fontSize}px "Segoe UI", sans-serif`;
}
ctx.fillText(title, width / 2, artY + artSize + 90);
ctx.fillStyle = '#b3b3b3';
let artistFontSize = 32;
ctx.font = `${artistFontSize}px "Segoe UI", sans-serif`;
while(ctx.measureText(artist).width > maxTextW && artistFontSize > 18) {
artistFontSize -= 2;
ctx.font = `${artistFontSize}px "Segoe UI", sans-serif`;
}
ctx.fillText(artist, width / 2, artY + artSize + 140);
// 5. Draw Progress Timeline
const waveX = artX;
const waveW = artSize;
const progY = 1000;
ctx.fillStyle = 'rgba(255,255,255,0.2)';
drawRoundRect(ctx, waveX, progY, waveW, 6, 3);
ctx.fill();
ctx.fillStyle = themeColor;
drawRoundRect(ctx, waveX, progY, waveW * 0.35, 6, 3);
ctx.fill();
ctx.beginPath();
ctx.arc(waveX + waveW * 0.35, progY + 3, 9, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(waveX + waveW * 0.35, progY + 3, 4, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#a0a0a0';
ctx.font = '16px "Segoe UI", sans-serif';
ctx.textAlign = 'left';
ctx.fillText('01:24', waveX, progY + 30);
ctx.textAlign = 'right';
ctx.fillText('03:45', waveX + waveW, progY + 30);
// 6. Audio Waveform / Spectrogram Visualization
const waveY = progY - 110;
const waveH = 70;
let waveformData = new Array(waveW).fill(0);
// Try parsing provided real audio file if available
if (audioUrl) {
try {
const response = await fetch(audioUrl);
const arrayBuffer = await response.arrayBuffer();
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const audioBuffer = await new Promise((resolve, reject) => {
const p = audioContext.decodeAudioData(arrayBuffer, resolve, reject);
if (p) p.then(resolve).catch(reject); // Promise-based or Callback-based compliance
});
const channelData = audioBuffer.getChannelData(0);
const step = Math.ceil(channelData.length / waveW);
for (let i = 0; i < waveW; i++) {
let min = 1.0, max = -1.0;
let start = i * step;
let end = Math.min((i + 1) * step, channelData.length);
for (let j = start; j < end; j++) {
if (channelData[j] < min) min = channelData[j];
if (channelData[j] > max) max = channelData[j];
}
waveformData[i] = Math.max(Math.abs(min), Math.abs(max));
}
// Simple smoothing kernel
let smoothed = new Array(waveW).fill(0);
for(let i = 0; i < waveW; i++) {
let sum = 0, count = 0;
for (let k = -2; k <= 2; k++) {
if (i + k >= 0 && i + k < waveW) {
sum += waveformData[i + k];
count++;
}
}
smoothed[i] = sum / count;
}
waveformData = smoothed;
} catch (e) {
console.warn("Could not load audio source. Resorting to HIFI visualizer baseline.", e);
}
}
// Dynamic HIFI aesthetic fake generation fallback
if (!waveformData.some(v => v > 0)) {
let phase1 = Math.random() * Math.PI;
let phase2 = Math.random() * Math.PI;
for (let i = 0; i < waveW; i++) {
let pos = i / waveW;
let env = 1 - Math.pow(Math.abs(pos - 0.5) * 2, 2);
let noise = (Math.random() * 0.4) + 0.6;
let wave = Math.abs(
Math.sin(pos * 50 + phase1) * 0.4 +
Math.cos(pos * 20 + phase2) * 0.4 +
Math.sin(pos * 100) * 0.2
);
waveformData[i] = wave * noise * Math.max(0, env);
}
}
// Actually Draw EQ Visualizer Bars
ctx.lineWidth = 3.5;
ctx.lineCap = 'round';
ctx.strokeStyle = themeColor;
for (let i = 0; i < waveW; i += 7) {
let amp = waveformData[i] || 0.02;
amp = Math.pow(amp, 0.8) * 1.5;
let h = Math.max(4, Math.min(amp * waveH, waveH));
ctx.beginPath();
ctx.moveTo(waveX + i, waveY + waveH/2 - h/2);
ctx.lineTo(waveX + i, waveY + waveH/2 + h/2);
ctx.stroke();
}
// 7. Draw Base Playback UI Controls
const controlsY = 1110;
ctx.fillStyle = '#ffffff';
// Play/Pause Big Round
ctx.beginPath();
ctx.arc(width/2, controlsY, 40, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(width/2 - 12, controlsY - 16);
ctx.lineTo(width/2 + 18, controlsY);
ctx.lineTo(width/2 - 12, controlsY + 16);
ctx.fill();
ctx.fillStyle = '#ffffff';
// Prev Track
const prevX = width/2 - 110;
ctx.beginPath();
ctx.moveTo(prevX + 10, controlsY - 14);
ctx.lineTo(prevX - 12, controlsY);
ctx.lineTo(prevX + 10, controlsY + 14);
ctx.fill();
ctx.fillRect(prevX - 17, controlsY - 14, 4, 28);
// Next Track
const nextX = width/2 + 110;
ctx.beginPath();
ctx.moveTo(nextX - 10, controlsY - 14);
ctx.lineTo(nextX + 12, controlsY);
ctx.lineTo(nextX - 10, controlsY + 14);
ctx.fill();
ctx.fillRect(nextX + 13, controlsY - 14, 4, 28);
return canvas;
}
Apply Changes