You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, scanRate = "3", volume = "0.5") {
// Main Container
const container = document.createElement('div');
container.style.fontFamily = 'Arial, sans-serif';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.gap = '15px';
container.style.padding = '20px';
container.style.background = '#2c2c2c';
container.style.color = '#fff';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 6px 16px rgba(0,0,0,0.6)';
container.style.maxWidth = '100%';
container.style.boxSizing = 'border-box';
// Title
const title = document.createElement('h2');
title.innerText = 'š³ Image to Kitchen Articles Voice Changer';
title.style.margin = '0';
title.style.fontSize = '22px';
container.appendChild(title);
// Visual Legend
const legend = document.createElement('div');
legend.innerHTML = `
<span style="color:#aaa;">šµ <b>Orchestra:</b></span>
<span style="color:#fff; background:#444; padding:2px 6px; border-radius:4px; margin-left:6px; font-size:12px;">Light: Clink</span>
<span style="color:#fff; background:#222; padding:2px 6px; border-radius:4px; margin-left:6px; font-size:12px;">Dark: Chop</span>
<span style="color:#fff; background:#666; padding:2px 6px; border-radius:4px; margin-left:6px; font-size:12px;">Gray: Metallic Clang</span>
<span style="color:#fff; background:#b43d3d; padding:2px 6px; border-radius:4px; margin-left:6px; font-size:12px;">Red: Sizzle</span>
<span style="color:#fff; background:#8f9a2b; padding:2px 6px; border-radius:4px; margin-left:6px; font-size:12px;">Yellow/Grn: Blender</span>
`;
legend.style.fontSize = '14px';
legend.style.textAlign = 'center';
container.appendChild(legend);
// Canvas wrapper & Canvas
const canvasWrapper = document.createElement('div');
canvasWrapper.style.position = 'relative';
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
canvas.style.maxWidth = '100%';
canvas.style.maxHeight = '60vh';
canvas.style.objectFit = 'contain';
canvas.style.border = '2px solid #555';
canvas.style.borderRadius = '6px';
canvas.style.background = '#000';
canvasWrapper.appendChild(canvas);
container.appendChild(canvasWrapper);
const ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.drawImage(originalImg, 0, 0);
// Cache image data for fast audio sampling
let imgData;
try {
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
} catch (e) {
console.error("Canvas extraction failed (probably cross-origin). Empty data fallback.", e);
imgData = new Uint8ClampedArray(canvas.width * canvas.height * 4);
}
const getPixel = (x, y) => {
const index = (Math.floor(y) * canvas.width + Math.floor(x)) * 4;
return {
r: imgData[index],
g: imgData[index + 1],
b: imgData[index + 2]
};
};
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h = 0, s = 0, l = (max + min) / 2;
if (max !== min) {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
// Audio State
let audioCtx, masterGain, noiseBuffer;
let isPlaying = false;
let scanX = 0;
let animationId;
let lastPlayedX = -100;
let activeNodes = [];
const speed = parseFloat(scanRate) || 3;
const volLvl = parseFloat(volume) || 0.5;
const initAudio = () => {
if (!audioCtx) {
const AudioContext = window.AudioContext || window.webkitAudioContext;
audioCtx = new AudioContext();
masterGain = audioCtx.createGain();
masterGain.gain.value = volLvl;
masterGain.connect(audioCtx.destination);
// Shared Noise buffer for sizzle effect
const bufferSize = audioCtx.sampleRate * 2;
noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
const output = noiseBuffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1;
}
}
if (audioCtx.state === 'suspended') audioCtx.resume();
};
// Kitchen Voice Synthesis Functions
const AudioKit = {
playSizzle: (intensity) => {
try {
const source = audioCtx.createBufferSource();
source.buffer = noiseBuffer;
const filter = audioCtx.createBiquadFilter();
filter.type = 'highpass';
filter.frequency.value = 2500 + Math.random() * 1000;
const gain = audioCtx.createGain();
const now = audioCtx.currentTime + 0.01;
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(0.4 * intensity, now + 0.05);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
source.connect(filter);
filter.connect(gain);
gain.connect(masterGain);
source.start(now);
source.stop(now + 0.3);
} catch (e) {}
},
playClink: (pitch) => {
try {
const osc = audioCtx.createOscillator();
osc.type = 'triangle';
const now = audioCtx.currentTime + 0.01;
osc.frequency.setValueAtTime(1500 + pitch * 2000, now);
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0.6, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
osc.connect(gain);
gain.connect(masterGain);
osc.start(now);
osc.stop(now + 0.15);
} catch (e) {}
},
playClang: (pitch) => {
try {
const freqs = [320, 480, 720]; // Metallic harmonics
const now = audioCtx.currentTime + 0.01;
freqs.forEach(f => {
const osc = audioCtx.createOscillator();
osc.type = 'square';
osc.frequency.value = f * (0.8 + pitch * 0.4);
const filter = audioCtx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.value = f * 1.5;
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0.2, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.4);
osc.connect(filter);
filter.connect(gain);
gain.connect(masterGain);
osc.start(now);
osc.stop(now + 0.4);
});
} catch (e) {}
},
playChop: (pitch) => {
try {
const osc = audioCtx.createOscillator();
osc.type = 'sawtooth';
const now = audioCtx.currentTime + 0.01;
osc.frequency.value = 80 + pitch * 100;
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0.7, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.08);
osc.connect(gain);
gain.connect(masterGain);
osc.start(now);
osc.stop(now + 0.08);
} catch (e) {}
},
playBlender: (pitch) => {
try {
const osc = audioCtx.createOscillator();
osc.type = 'sawtooth';
const now = audioCtx.currentTime + 0.01;
osc.frequency.setValueAtTime(150 + pitch * 100, now);
osc.frequency.linearRampToValueAtTime(200 + pitch * 100, now + 0.2);
const gain = audioCtx.createGain();
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(0.2, now + 0.1);
gain.gain.linearRampToValueAtTime(0, now + 0.3);
osc.connect(gain);
gain.connect(masterGain);
osc.start(now);
osc.stop(now + 0.3);
} catch (e) {}
}
};
const processPixelAudio = (y) => {
const p = getPixel(scanX, y);
if (p.r === undefined || (p.r === 0 && p.g === 0 && p.b === 0)) return;
const [h, s, l] = rgbToHsl(p.r, p.g, p.b);
const hueDeg = Math.round(h * 360);
if (l < 0.2) {
AudioKit.playChop(l);
} else if (l > 0.8) {
AudioKit.playClink(l);
} else if (s < 0.2) {
AudioKit.playClang(l);
} else {
if (hueDeg < 50 || hueDeg > 330) {
AudioKit.playSizzle(s); // Reds & Oranges
} else if (hueDeg >= 50 && hueDeg < 160) {
AudioKit.playBlender(l); // Yellows & Greens
} else {
AudioKit.playClang(l); // Blues/Purples
}
}
// Add visual bubble pop effect
activeNodes.push({ x: scanX, y, color: `hsl(${hueDeg}, ${s*100}%, ${l*100}%)`, life: 1.0 });
};
function drawFrame() {
if (!isPlaying) return;
// Reset canvas frame
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImg, 0, 0);
// Draw Scanline
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.fillRect(scanX, 0, 2, canvas.height);
// Trigger sounds at spatial intervals
if (scanX - lastPlayedX >= 15 || scanX < lastPlayedX) {
lastPlayedX = scanX;
const numSamples = 3; // 3 sounds played concurrently (chords)
for (let i = 1; i <= numSamples; i++) {
const y = canvas.height * (i / (numSamples + 1));
processPixelAudio(Math.floor(y));
}
}
// Render Animated Nodes
for (let i = activeNodes.length - 1; i >= 0; i--) {
const node = activeNodes[i];
ctx.beginPath();
ctx.arc(node.x, node.y, 16 - (node.life * 12), 0, Math.PI * 2);
ctx.fillStyle = node.color;
ctx.globalAlpha = node.life;
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = '#fff';
ctx.stroke();
ctx.globalAlpha = 1.0;
node.life -= 0.05;
if (node.life <= 0) activeNodes.splice(i, 1);
}
scanX += speed;
if (scanX >= canvas.width) scanX = 0;
animationId = requestAnimationFrame(drawFrame);
}
// Controls UI
const controls = document.createElement('div');
controls.style.display = 'flex';
controls.style.gap = '15px';
controls.style.alignItems = 'center';
const playBtn = document.createElement('button');
playBtn.innerText = 'ā¶ Start Kitchen Voice Symphony';
playBtn.style.padding = '10px 20px';
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 = '15px';
controls.appendChild(playBtn);
const stopBtn = document.createElement('button');
stopBtn.innerText = 'ā¹ Stop';
stopBtn.style.padding = '10px 20px';
stopBtn.style.background = '#888';
stopBtn.style.color = '#fff';
stopBtn.style.border = 'none';
stopBtn.style.borderRadius = '5px';
stopBtn.style.cursor = 'pointer';
stopBtn.style.fontWeight = 'bold';
stopBtn.style.fontSize = '15px';
controls.appendChild(stopBtn);
container.appendChild(controls);
playBtn.onclick = () => {
initAudio();
if (!isPlaying) {
isPlaying = true;
playBtn.style.background = '#666';
stopBtn.style.background = '#f44336';
drawFrame();
}
};
stopBtn.onclick = () => {
isPlaying = false;
playBtn.style.background = '#4CAF50';
stopBtn.style.background = '#888';
cancelAnimationFrame(animationId);
activeNodes = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImg, 0, 0); // Reset visual state
};
return container;
}
Apply Changes