You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, filterType = 'lowpass', defaultFreq = 1000) {
const freqValue = Number(defaultFreq) || 1000;
const validFilters = ['lowpass', 'highpass', 'bandpass', 'lowshelf', 'highshelf', 'peaking', 'notch', 'allpass'];
const fType = validFilters.includes(filterType) ? filterType : 'lowpass';
const container = document.createElement('div');
container.style.position = 'relative';
container.style.display = 'inline-block';
container.style.fontFamily = 'Arial, sans-serif';
const cWidth = Math.max(originalImg.width, 320);
const cHeight = Math.max(originalImg.height, 420);
container.style.width = cWidth + 'px';
container.style.maxWidth = '100%';
container.style.boxShadow = '0px 4px 15px rgba(0,0,0,0.5)';
container.style.borderRadius = '8px';
container.style.overflow = 'hidden';
// Set up visualizer canvas
const canvas = document.createElement('canvas');
canvas.width = cWidth;
canvas.height = cHeight;
canvas.style.display = 'block';
canvas.style.width = '100%';
canvas.style.height = 'auto';
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
// Calculate best fit logic for cover art (original image)
const imgRatio = originalImg.width / originalImg.height;
const cRatio = cWidth / cHeight;
let drawW = cWidth;
let drawH = cHeight;
let drawX = 0;
let drawY = 0;
if (imgRatio > cRatio) {
drawH = cWidth / imgRatio;
drawY = (cHeight - drawH) / 2;
} else {
drawW = cHeight * imgRatio;
drawX = (cWidth - drawW) / 2;
}
function renderBaseImage() {
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImg, drawX, drawY, drawW, drawH);
}
// Initial Render
renderBaseImage();
// UI Controls Frame
const controls = document.createElement('div');
controls.style.position = 'absolute';
controls.style.bottom = '10px';
controls.style.left = '10px';
controls.style.width = 'calc(100% - 20px)';
controls.style.boxSizing = 'border-box';
controls.style.background = 'rgba(20, 20, 20, 0.85)';
controls.style.padding = '15px';
controls.style.borderRadius = '8px';
controls.style.color = '#fff';
controls.style.display = 'flex';
controls.style.flexDirection = 'column';
controls.style.gap = '12px';
controls.style.zIndex = '10';
container.appendChild(controls);
// Title
const title = document.createElement('div');
title.innerText = 'Audio Filter Tool';
title.style.fontWeight = 'bold';
title.style.fontSize = '16px';
title.style.textAlign = 'center';
title.style.marginBottom = '5px';
controls.appendChild(title);
// Audio Input
const row1 = document.createElement('div');
row1.style.display = 'flex';
row1.style.justifyContent = 'space-between';
row1.style.alignItems = 'center';
const lblSrc = document.createElement('span');
lblSrc.innerText = 'Audio Source:';
lblSrc.style.fontSize = '14px';
row1.appendChild(lblSrc);
const fileIn = document.createElement('input');
fileIn.type = 'file';
fileIn.accept = 'audio/*';
fileIn.style.width = '160px';
fileIn.style.fontSize = '12px';
row1.appendChild(fileIn);
controls.appendChild(row1);
// Filter Type Selector
const row2 = document.createElement('div');
row2.style.display = 'flex';
row2.style.justifyContent = 'space-between';
row2.style.alignItems = 'center';
const lblType = document.createElement('span');
lblType.innerText = 'Filter Type:';
lblType.style.fontSize = '14px';
row2.appendChild(lblType);
const selType = document.createElement('select');
selType.style.width = '120px';
validFilters.forEach(f => {
const op = document.createElement('option');
op.value = f;
op.innerText = f;
if(f === fType) op.selected = true;
selType.appendChild(op);
});
row2.appendChild(selType);
controls.appendChild(row2);
// Frequency Slider
const row3 = document.createElement('div');
row3.style.display = 'flex';
row3.style.justifyContent = 'space-between';
row3.style.alignItems = 'center';
const lblFreq = document.createElement('span');
lblFreq.innerText = `Freq (${freqValue}Hz):`;
lblFreq.style.fontSize = '14px';
row3.appendChild(lblFreq);
const rngFreq = document.createElement('input');
rngFreq.type = 'range';
rngFreq.min = '20';
rngFreq.max = '20000';
rngFreq.value = freqValue;
rngFreq.style.width = '120px';
row3.appendChild(rngFreq);
controls.appendChild(row3);
// Resonance Slider (Q)
const row4 = document.createElement('div');
row4.style.display = 'flex';
row4.style.justifyContent = 'space-between';
row4.style.alignItems = 'center';
const lblQ = document.createElement('span');
lblQ.innerText = `Q (Resonance):`;
lblQ.style.fontSize = '14px';
row4.appendChild(lblQ);
const rngQ = document.createElement('input');
rngQ.type = 'range';
rngQ.min = '0';
rngQ.max = '30';
rngQ.step = '0.5';
rngQ.value = '5';
rngQ.style.width = '120px';
row4.appendChild(rngQ);
controls.appendChild(row4);
// Play/Pause Button
const playBtn = document.createElement('button');
playBtn.innerText = 'Play Synth Drone';
playBtn.style.padding = '10px';
playBtn.style.background = '#4CAF50';
playBtn.style.color = '#fff';
playBtn.style.border = 'none';
playBtn.style.borderRadius = '5px';
playBtn.style.cursor = 'pointer';
playBtn.style.fontWeight = 'bold';
playBtn.style.fontSize = '14px';
controls.appendChild(playBtn);
// Web Audio API Elements
let audioCtx;
let source;
let filter;
let analyser;
let dataArray;
let bufferLength;
let isPlaying = false;
let audioBuffer = null;
// Drawing visually responsive spectrum
function drawVisualizer() {
if (!isPlaying) return;
requestAnimationFrame(drawVisualizer);
analyser.getByteFrequencyData(dataArray);
renderBaseImage();
ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const ctrlHeight = controls.offsetHeight;
const maxBarY = canvas.height - ctrlHeight - 15;
const maxBarH = canvas.height * 0.55;
const barWidth = (canvas.width / bufferLength) * 2;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] / 255) * maxBarH;
const r = barHeight + (25 * (i / bufferLength));
const g = 250 * (i / bufferLength);
const b = 150;
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(x, maxBarY - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}
// Connect interactions
fileIn.addEventListener('change', (e) => {
const file = e.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.onload = (evt) => {
const arrayBuffer = evt.target.result;
if(!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
audioCtx.decodeAudioData(arrayBuffer, (buffer) => {
audioBuffer = buffer;
if(source) {
try { source.stop(); } catch(err) {}
source = null;
}
if(isPlaying) playBtn.click();
playBtn.innerText = 'Play Uploaded Audio';
});
};
reader.readAsArrayBuffer(file);
});
rngFreq.addEventListener('input', (e) => {
lblFreq.innerText = `Freq (${e.target.value}Hz):`;
if (filter && audioCtx) filter.frequency.setValueAtTime(e.target.value, audioCtx.currentTime);
});
rngQ.addEventListener('input', (e) => {
if (filter && audioCtx) filter.Q.setValueAtTime(e.target.value, audioCtx.currentTime);
});
selType.addEventListener('change', (e) => {
if (filter) filter.type = e.target.value;
});
playBtn.addEventListener('click', () => {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if (isPlaying) {
if (audioCtx.state === 'running') audioCtx.suspend();
playBtn.innerText = audioBuffer ? 'Play Uploaded Audio' : 'Play Synth Drone';
playBtn.style.background = '#4CAF50';
isPlaying = false;
setTimeout(renderBaseImage, 50); // Restore raw image display
} else {
if (!source) {
if (audioCtx.state === 'suspended') audioCtx.resume();
filter = audioCtx.createBiquadFilter();
filter.type = selType.value;
filter.frequency.setValueAtTime(rngFreq.value, audioCtx.currentTime);
filter.Q.setValueAtTime(rngQ.value, audioCtx.currentTime);
analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
if (audioBuffer) { // User Audio
source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
source.loop = true;
} else { // Synthesized Tone (Sawtooth at C3)
source = audioCtx.createOscillator();
source.type = 'sawtooth';
source.frequency.setValueAtTime(130.81, audioCtx.currentTime);
}
source.connect(filter);
filter.connect(analyser);
const gainNode = audioCtx.createGain();
gainNode.gain.setValueAtTime(0.6, audioCtx.currentTime); // Prevent blasting volume
analyser.connect(gainNode);
gainNode.connect(audioCtx.destination);
source.start(0);
} else {
if (audioCtx.state === 'suspended') audioCtx.resume();
}
playBtn.innerText = 'Pause Filter';
playBtn.style.background = '#f44336';
isPlaying = true;
drawVisualizer();
}
});
return container;
}
Apply Changes