You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, durationSeconds = "5", minFrequency = "100", maxFrequency = "10000", timeResolution = "300", freqResolution = "100") {
const durationSec = Number(durationSeconds);
const minFreq = Number(minFrequency);
const maxFreq = Number(maxFrequency);
const timeResN = Math.max(1, Number(timeResolution));
const freqResN = Math.max(1, Number(freqResolution));
// Create the container UI
const container = document.createElement('div');
container.style.fontFamily = 'system-ui, sans-serif';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.gap = '20px';
container.style.padding = '30px';
container.style.background = '#1a1a1a';
container.style.color = '#ffffff';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.4)';
container.style.width = '100%';
container.style.boxSizing = 'border-box';
// Initial loading state UI
const title = document.createElement('div');
title.textContent = 'Preparing Audio Generator...';
title.style.fontWeight = 'bold';
title.style.fontSize = '20px';
container.appendChild(title);
// Process asynchronously to avoid freezing the browser interface
setTimeout(async () => {
title.textContent = 'Extracting Image Data...';
const sampleRate = 44100;
const N = Math.floor(durationSec * sampleRate);
const buffer = new Float32Array(N);
const ctx = document.createElement('canvas').getContext('2d', { willReadFrequently: true });
ctx.canvas.width = timeResN;
ctx.canvas.height = freqResN;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, timeResN, freqResN);
ctx.drawImage(originalImg, 0, 0, timeResN, freqResN);
const imgData = ctx.getImageData(0, 0, timeResN, freqResN).data;
const luminanceGrid = new Float32Array(freqResN * timeResN);
for (let y = 0; y < freqResN; y++) {
for (let x = 0; x < timeResN; x++) {
const idx = (y * timeResN + x) * 4;
let r = imgData[idx] / 255;
let g = imgData[idx + 1] / 255;
let b = imgData[idx + 2] / 255;
// Emphasize brightness using non-linear scale (squared) to reduce noise floor
let lum = (r * 0.299 + g * 0.587 + b * 0.114);
luminanceGrid[y * timeResN + x] = lum * lum;
}
}
title.textContent = 'Synthesizing Spectrogram...';
const progressBarWrap = document.createElement('div');
progressBarWrap.style.width = '100%';
progressBarWrap.style.maxWidth = '600px';
progressBarWrap.style.height = '12px';
progressBarWrap.style.background = '#333';
progressBarWrap.style.borderRadius = '6px';
progressBarWrap.style.overflow = 'hidden';
const progressBar = document.createElement('div');
progressBar.style.width = '0%';
progressBar.style.height = '100%';
progressBar.style.background = '#e74c3c';
progressBar.style.transition = 'width 0.1s linear';
progressBarWrap.appendChild(progressBar);
container.appendChild(progressBarWrap);
const xStep = (timeResN - 1) / N;
const maxFreqIndex = Math.max(1, freqResN - 1);
await new Promise(resolve => {
let y = 0;
// Process the generative synthesis in batches
function processChunk() {
const startY = y;
const endY = Math.min(y + 5, freqResN);
for (; y < endY; y++) {
const freq = minFreq * Math.pow(maxFreq / minFreq, (maxFreqIndex - y) / maxFreqIndex);
let phase = 0;
const phaseInc = 2 * Math.PI * freq / sampleRate;
const rowOffset = y * timeResN;
for (let i = 0; i < N; i++) {
const x = i * xStep;
const x0 = Math.floor(x);
const x1 = Math.min(x0 + 1, timeResN - 1);
const frac = x - x0;
const lum0 = luminanceGrid[rowOffset + x0];
const lum1 = luminanceGrid[rowOffset + x1];
const amplitude = lum0 * (1 - frac) + lum1 * frac;
buffer[i] += Math.sin(phase) * amplitude;
phase = (phase + phaseInc) % (2 * Math.PI);
}
}
progressBar.style.width = `${(y / freqResN) * 100}%`;
if (y < freqResN) {
setTimeout(processChunk, 10);
} else {
resolve();
}
}
processChunk();
});
title.textContent = 'Finalizing Audio File...';
// Normalize buffer
let maxAmp = 0;
for (let i = 0; i < N; i++) {
const absVal = Math.abs(buffer[i]);
if (absVal > maxAmp) maxAmp = absVal;
}
if (maxAmp > 0) {
for (let i = 0; i < N; i++) {
buffer[i] = (buffer[i] / maxAmp) * 0.9;
}
}
// WAV Header Generation (16-bit PCM Mono)
const bufferLength = buffer.length;
const wavFile = new Uint8Array(44 + bufferLength * 2);
const view = new DataView(wavFile.buffer);
const writeString = (view, offset, string) => {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};
writeString(view, 0, 'RIFF');
view.setUint32(4, 36 + bufferLength * 2, true);
writeString(view, 8, 'WAVE');
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true); // PCM Format
view.setUint16(22, 1, true); // Mono
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * 2, true); // Byte rate
view.setUint16(32, 2, true); // Block align
view.setUint16(34, 16, true); // Bits per sample
writeString(view, 36, 'data');
view.setUint32(40, bufferLength * 2, true);
// PCM Writing
let offset = 44;
for (let i = 0; i < bufferLength; i++) {
let s = Math.max(-1, Math.min(1, buffer[i]));
view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
offset += 2;
}
const blob = new Blob([view], { type: 'audio/wav' });
const audioUrl = URL.createObjectURL(blob);
container.innerHTML = '';
// Switch to the Audio Player user interface
const finalTitle = document.createElement('div');
finalTitle.textContent = 'Audio Output Ready';
finalTitle.style.fontWeight = 'bold';
finalTitle.style.fontSize = '20px';
finalTitle.style.marginBottom = '10px';
container.appendChild(finalTitle);
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
canvas.style.maxWidth = '100%';
canvas.style.maxHeight = '450px';
canvas.style.background = '#000';
canvas.style.borderRadius = '8px';
canvas.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.4)';
const outCtx = canvas.getContext('2d');
outCtx.drawImage(originalImg, 0, 0);
const audioEle = document.createElement('audio');
audioEle.controls = true;
audioEle.src = audioUrl;
audioEle.style.width = '100%';
audioEle.style.maxWidth = '600px';
audioEle.style.marginTop = '20px';
let isPlaying = false;
let animationFrameId;
function drawPlayhead() {
outCtx.drawImage(originalImg, 0, 0);
let duration = audioEle.duration;
if (!duration || isNaN(duration)) duration = durationSec;
let progress = audioEle.currentTime / duration;
if (progress > 0 && progress <= 1) {
const x = progress * canvas.width;
outCtx.beginPath();
outCtx.moveTo(x, 0);
outCtx.lineTo(x, canvas.height);
outCtx.lineWidth = Math.max(2, canvas.width / 400);
outCtx.strokeStyle = 'rgba(255, 75, 75, 0.9)';
outCtx.shadowColor = 'rgba(0, 0, 0, 0.5)';
outCtx.shadowBlur = 5;
outCtx.stroke();
// Reset shadow values immediately
outCtx.shadowColor = 'transparent';
outCtx.shadowBlur = 0;
}
}
function renderFrame() {
if (!isPlaying) return;
drawPlayhead();
animationFrameId = requestAnimationFrame(renderFrame);
}
audioEle.addEventListener('play', () => {
isPlaying = true;
renderFrame();
});
audioEle.addEventListener('pause', () => {
isPlaying = false;
cancelAnimationFrame(animationFrameId);
drawPlayhead();
});
audioEle.addEventListener('seeked', () => {
drawPlayhead();
});
audioEle.addEventListener('ended', () => {
isPlaying = false;
cancelAnimationFrame(animationFrameId);
outCtx.drawImage(originalImg, 0, 0);
});
container.appendChild(canvas);
container.appendChild(audioEle);
}, 100);
return container;
}
Apply Changes