You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, audioUrl = "", visualizationStyle = "bars", waveColor = "#ffffff", overlayColor = "rgba(0, 0, 0, 0.5)", titleText = "Audio Track Visualization") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Draw the image as background
ctx.drawImage(originalImg, 0, 0);
// Apply semi-transparent overlay to make waveform stand out
ctx.fillStyle = overlayColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
let decodedAudio = null;
// Attempt to fetch and decode the audio file if a URL/base64 is provided
if (audioUrl && audioUrl.trim() !== "") {
try {
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();
const response = await fetch(audioUrl);
const arrayBuffer = await response.arrayBuffer();
decodedAudio = await audioCtx.decodeAudioData(arrayBuffer);
} catch (err) {
console.warn("Failed to decode audio. Using fallback visualizer.", err);
}
}
ctx.fillStyle = waveColor;
ctx.strokeStyle = waveColor;
// Layout configurations
const padding = canvas.width * 0.1;
const drawWidth = canvas.width - (padding * 2);
const drawHeight = canvas.height * 0.4;
const midY = canvas.height / 2.2;
const type = visualizationStyle.toLowerCase() === "waveform" ? "waveform" : "bars";
if (decodedAudio) {
const data = decodedAudio.getChannelData(0);
if (type === "bars") {
const numBars = Math.min(150, Math.floor(drawWidth / 4));
const barWidth = drawWidth / numBars;
const step = Math.floor(data.length / numBars);
for (let i = 0; i < numBars; i++) {
let sum = 0;
for (let j = 0; j < step; j++) {
sum += Math.abs(data[(i * step) + j]);
}
const avg = sum / step;
// Scale up the bar height
let h = avg * drawHeight * 4;
if (h > drawHeight) h = drawHeight;
if (h < 2) h = 2;
const x = padding + (i * barWidth) + (barWidth * 0.1);
const w = barWidth * 0.8;
if (ctx.roundRect) {
ctx.beginPath();
ctx.roundRect(x, midY - h/2, w, h, w/2);
ctx.fill();
} else {
ctx.fillRect(x, midY - h/2, w, h);
}
}
} else {
// Continuous Waveform Visualization
const step = Math.ceil(data.length / drawWidth);
ctx.lineWidth = Math.max(1, drawWidth / 800);
ctx.beginPath();
for (let i = 0; i < drawWidth; i++) {
let min = 1.0;
let max = -1.0;
for (let j = 0; j < step; j++) {
const idx = (i * step) + j;
if (idx < data.length) {
const datum = data[idx];
if (datum < min) min = datum;
if (datum > max) max = datum;
}
}
const x = padding + i;
const yMin = midY + (min * (drawHeight/2));
const yMax = midY + (max * (drawHeight/2));
if (i === 0) ctx.moveTo(x, yMin);
else ctx.lineTo(x, yMin);
ctx.lineTo(x, yMax);
}
ctx.stroke();
}
} else {
// Fallback Generative Pattern (Used if no audioUrl or if decode failed)
// Helps to visually represent what the tool does.
if (type === "bars") {
const numBars = 80;
const barWidth = drawWidth / numBars;
for (let i = 0; i < numBars; i++) {
// Generative envelope maps sine forms to emulate a typical track intensity
const env = Math.pow(Math.sin((i / numBars) * Math.PI), 0.6);
const noise = 0.2 + 0.8 * Math.random();
let h = env * noise * (drawHeight * 0.9);
if (h < 4) h = 4;
const x = padding + (i * barWidth) + (barWidth * 0.1);
const w = barWidth * 0.8;
if (ctx.roundRect) {
ctx.beginPath();
ctx.roundRect(x, midY - h/2, w, h, w/2);
ctx.fill();
} else {
ctx.fillRect(x, midY - h/2, w, h);
}
}
} else {
ctx.lineWidth = Math.max(2, drawWidth / 500);
ctx.beginPath();
for (let i = 0; i < drawWidth; i++) {
const ratio = i / drawWidth;
const env = Math.pow(Math.sin(ratio * Math.PI), 0.7);
const noise = (Math.random() * 2 - 1);
const val = env * noise * (drawHeight / 2);
const x = padding + i;
const y1 = midY - Math.abs(val);
const y2 = midY + Math.abs(val);
ctx.moveTo(x, y1);
ctx.lineTo(x, y2);
}
ctx.stroke();
}
}
// Add Title Text if provided
if (titleText && titleText.trim() !== "") {
ctx.fillStyle = waveColor;
const fontSize = Math.max(16, Math.floor(canvas.height * 0.06));
ctx.font = `bold ${fontSize}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const textY = midY + (drawHeight / 2) + (canvas.height * 0.1);
ctx.fillText(titleText, canvas.width / 2, textY);
}
return canvas;
}
Apply Changes