You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, animalType = "auto", effectIntensity = "1.0") {
// Determine scale and size for the display
const MAX_WIDTH = 600;
const scale = Math.min(1, MAX_WIDTH / originalImg.width);
const canvasWidth = originalImg.width * scale;
const canvasHeight = originalImg.height * scale;
// Create main container
const container = document.createElement("div");
container.style.position = "relative";
container.style.display = "inline-block";
container.style.fontFamily = "system-ui, -apple-system, sans-serif";
container.style.overflow = "hidden";
container.style.borderRadius = "8px";
container.style.boxShadow = "0 4px 6px rgba(0,0,0,0.1)";
container.style.width = canvasWidth + "px";
container.style.height = canvasHeight + "px";
container.style.backgroundColor = "#000";
// Setup Canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
container.appendChild(canvas);
// Image Analysis for Animal Type Detection
let activeAnimal = animalType.toLowerCase();
if (activeAnimal === "auto") {
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
let r = 0, g = 0, b = 0;
// Sample pixels to compute average color
for (let i = 0; i < imgData.length; i += 16) { // Sample every 4th pixel for speed
r += imgData[i];
g += imgData[i + 1];
b += imgData[i + 2];
}
const pixels = imgData.length / 16;
r /= pixels; g /= pixels; b /= pixels;
const avg = (r + g + b) / 3;
const max = Math.max(r, g, b);
if (max === g && g > r + 20 && g > b + 20) activeAnimal = "frog";
else if (max === b && b > r + 30 && b > g + 30) activeAnimal = "bird";
else if (avg < 80) activeAnimal = "bear";
else if (avg > 180) activeAnimal = "cat";
else activeAnimal = "dog";
}
const validAnimals = ["cat", "dog", "bear", "bird", "frog"];
if (!validAnimals.includes(activeAnimal)) activeAnimal = "cat";
// Badge showing current status
const badge = document.createElement("div");
badge.style.position = "absolute";
badge.style.top = "10px";
badge.style.left = "10px";
badge.style.backgroundColor = "rgba(0,0,0,0.6)";
badge.style.color = "white";
badge.style.padding = "5px 10px";
badge.style.borderRadius = "20px";
badge.style.fontSize = "12px";
badge.style.zIndex = "10";
badge.innerText = `Detected Mode: ${activeAnimal.toUpperCase()}`;
container.appendChild(badge);
// Create UI controls
const uiContainer = document.createElement("div");
uiContainer.style.position = "absolute";
uiContainer.style.bottom = "0";
uiContainer.style.left = "0";
uiContainer.style.width = "100%";
uiContainer.style.backgroundColor = "rgba(0,0,0,0.75)";
uiContainer.style.padding = "15px";
uiContainer.style.boxSizing = "border-box";
uiContainer.style.display = "flex";
uiContainer.style.flexDirection = "column";
uiContainer.style.alignItems = "center";
uiContainer.style.gap = "10px";
// Selector row
const topRow = document.createElement("div");
topRow.style.color = "#fff";
topRow.style.fontSize = "14px";
topRow.style.display = "flex";
topRow.style.alignItems = "center";
topRow.style.gap = "10px";
const label = document.createElement("label");
label.innerText = "Voice Effect:";
const select = document.createElement("select");
select.style.padding = "4px 8px";
select.style.borderRadius = "4px";
select.style.cursor = "pointer";
select.style.backgroundColor = "#fff";
validAnimals.forEach(animal => {
const option = document.createElement("option");
option.value = animal;
option.innerText = animal.charAt(0).toUpperCase() + animal.slice(1);
if (animal === activeAnimal) option.selected = true;
select.appendChild(option);
});
select.onchange = (e) => {
activeAnimal = e.target.value;
badge.innerText = `Selected Mode: ${activeAnimal.toUpperCase()}`;
};
topRow.appendChild(label);
topRow.appendChild(select);
uiContainer.appendChild(topRow);
// Buttons row
const btnRow = document.createElement("div");
btnRow.style.display = "flex";
btnRow.style.gap = "10px";
const btnStyle = "padding: 10px 20px; border: none; border-radius: 20px; font-weight: bold; cursor: pointer; transition: all 0.2s; white-space: nowrap; user-select: none; -webkit-user-select: none;";
const recordBtn = document.createElement("button");
recordBtn.innerHTML = "🎤 Hold to Record Voice";
recordBtn.style.cssText = btnStyle + "background-color: #ff4757; color: white;";
const playBtn = document.createElement("button");
playBtn.innerHTML = "â–¶ Play Back Effect";
playBtn.style.cssText = btnStyle + "background-color: #2ed573; color: white; display: none;";
btnRow.appendChild(recordBtn);
btnRow.appendChild(playBtn);
uiContainer.appendChild(btnRow);
container.appendChild(uiContainer);
// Audio Processing Logic
let mediaRecorder;
let audioChunks = [];
let audioContext;
let recordedAudioBuffer = null;
let isRecording = false;
let isPlaying = false;
const startRecording = async () => {
if (isRecording) return;
try {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioContext.state === "suspended") {
await audioContext.resume();
}
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = e => { if (e.data.size > 0) audioChunks.push(e.data); };
mediaRecorder.onstart = () => {
isRecording = true;
recordBtn.innerHTML = "🔴 Recording...";
recordBtn.style.backgroundColor = "#ff6b81";
playBtn.style.display = "none";
};
mediaRecorder.onstop = async () => {
isRecording = false;
recordBtn.innerHTML = "🎤 Hold to Record Voice";
recordBtn.style.backgroundColor = "#ff4757";
if (audioChunks.length === 0) return;
const audioBlob = new Blob(audioChunks, { type: mediaRecorder.mimeType });
const arrayBuffer = await audioBlob.arrayBuffer();
audioContext.decodeAudioData(arrayBuffer, (buffer) => {
recordedAudioBuffer = buffer;
playBtn.style.display = "block";
playModifiedAudio();
}, (err) => {
console.error("Audio decoding failed. The recording might have been too short. ", err);
});
};
mediaRecorder.start();
} catch (e) {
alert("Microphone access is required for the Voice Changer.");
}
};
const stopRecording = () => {
if (mediaRecorder && mediaRecorder.state !== "inactive") {
mediaRecorder.stop();
mediaRecorder.stream.getTracks().forEach(track => track.stop());
}
};
// Event listeners to handle mouse and touch
recordBtn.addEventListener("mousedown", startRecording);
recordBtn.addEventListener("mouseup", stopRecording);
recordBtn.addEventListener("mouseleave", stopRecording);
recordBtn.addEventListener("touchstart", (e) => { e.preventDefault(); startRecording(); }, { passive: false });
recordBtn.addEventListener("touchend", (e) => { e.preventDefault(); stopRecording(); }, { passive: false });
playBtn.addEventListener("click", () => {
if (recordedAudioBuffer) playModifiedAudio();
});
function playModifiedAudio() {
if (isPlaying || !recordedAudioBuffer) return;
isPlaying = true;
const intensity = parseFloat(effectIntensity) || 1.0;
const source = audioContext.createBufferSource();
source.buffer = recordedAudioBuffer;
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
let targetNode = source;
// Apply animal effect filters and speed shifts
switch(activeAnimal) {
case "cat": // High pitch chipmunk
source.playbackRate.value = 1.4 + (0.2 * intensity);
const highpass = audioContext.createBiquadFilter();
highpass.type = "highpass";
highpass.frequency.value = 600 * intensity;
source.connect(highpass);
targetNode = highpass;
break;
case "dog": // Lower, rougher
source.playbackRate.value = 0.75 - (0.1 * intensity);
const lowpass = audioContext.createBiquadFilter();
lowpass.type = "lowpass";
lowpass.frequency.value = 1200 / intensity;
source.connect(lowpass);
targetNode = lowpass;
break;
case "bear": // Very low / slowed down
source.playbackRate.value = 0.5 - (0.1 * intensity);
const bearLowpass = audioContext.createBiquadFilter();
bearLowpass.type = "lowpass";
bearLowpass.frequency.value = 800;
source.connect(bearLowpass);
targetNode = bearLowpass;
break;
case "bird": // Fast and twangy resonance
source.playbackRate.value = 1.7 + (0.3 * intensity);
const bpeq = audioContext.createBiquadFilter();
bpeq.type = "peaking";
bpeq.frequency.value = 3000;
bpeq.Q.value = 5;
bpeq.gain.value = 15;
source.connect(bpeq);
targetNode = bpeq;
break;
case "frog": // Croaky (Tremolo effect modulated on gain)
source.playbackRate.value = 0.85;
const tremoloGain = audioContext.createGain();
const lfo = audioContext.createOscillator();
lfo.type = "sine";
lfo.frequency.value = 18 * intensity;
const lfoGain = audioContext.createGain();
lfoGain.gain.value = 0.5;
lfo.connect(lfoGain);
tremoloGain.gain.value = 0.5;
lfoGain.connect(tremoloGain.gain);
source.connect(tremoloGain);
lfo.start();
source.onended = () => { lfo.stop(); isPlaying = false; };
targetNode = tremoloGain;
break;
}
if (activeAnimal !== "frog") {
source.onended = () => { isPlaying = false; };
}
targetNode.connect(analyser);
analyser.connect(audioContext.destination);
source.start();
drawVisualizer(analyser);
}
function drawVisualizer(analyser) {
if (!isPlaying) {
// Restore original image once playback drops
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
return;
}
requestAnimationFrame(() => drawVisualizer(analyser));
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
// Draw image backdrop mapping audio visual overlay
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = (canvas.width / bufferLength) * 2;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = dataArray[i];
// Color mapping based on chosen creature
let hue = 0;
switch(activeAnimal) {
case "cat": hue = 350; break;
case "dog": hue = 30; break;
case "bear": hue = 0; break;
case "bird": hue = 200; break;
case "frog": hue = 120; break;
}
ctx.fillStyle = `hsl(${hue}, 100%, ${activeAnimal === 'bear' ? barHeight / 4 : barHeight / 2}%)`;
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}
return container;
}
Apply Changes