You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* This function provides a creative, visual interpretation of an "Image Voice Changer".
* Since images do not have a voice, this tool applies visual effects to an image
* that are metaphorically named after common audio effects.
*
* @param {Image} originalImg The original JavaScript Image object.
* @param {string} effectName The desired visual effect. Accepted values are:
* 'none', 'echo', 'reverb', 'distortion', 'pitch', 'robot', 'flanger'.
* @param {number} intensity The strength of the effect, from 0.0 (none) to 1.0 (max).
* @returns {HTMLCanvasElement} A canvas element displaying the modified image.
*/
function processImage(originalImg, effectName = 'distortion', intensity = 0.5) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
// Clamp intensity to the valid range [0, 1]
const clampedIntensity = Math.max(0, Math.min(1, intensity));
// --- Helper Functions for Visual Effects ---
/**
* VISUAL ECHO: Draws delayed, fading copies of the image.
*/
const applyEcho = () => {
ctx.clearRect(0, 0, width, height);
const numEchoes = Math.max(2, Math.floor(clampedIntensity * 8));
const maxOffset = Math.min(width, height) * 0.1;
for (let i = numEchoes - 1; i > 0; i--) {
ctx.globalAlpha = 0.5 / i;
const offset = (maxOffset / numEchoes) * i;
ctx.drawImage(originalImg, offset, offset);
}
ctx.globalAlpha = 1.0;
ctx.drawImage(originalImg, 0, 0);
};
/**
* VISUAL REVERB: Applies a blur filter, smearing the image like sound in a large room.
*/
const applyReverb = () => {
const blurRadius = clampedIntensity * 15;
ctx.filter = `blur(${blurRadius}px)`;
ctx.drawImage(originalImg, 0, 0);
ctx.filter = 'none'; // Reset filter for other drawings
};
/**
* VISUAL DISTORTION: Applies a wave effect, distorting the image like a distorted guitar.
*/
const applyDistortion = () => {
ctx.drawImage(originalImg, 0, 0);
const sourceData = ctx.getImageData(0, 0, width, height);
const destData = ctx.createImageData(width, height);
const sourcePixels = sourceData.data;
const destPixels = destData.data;
const amplitude = height * 0.1 * clampedIntensity;
const frequency = 10 * clampedIntensity + 3;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const offsetX = amplitude * Math.sin((y / height) * frequency * Math.PI * 2);
const sx = Math.round(x + offsetX);
const sy = y;
const destIndex = (y * width + x) * 4;
if (sx >= 0 && sx < width) {
const sourceIndex = (sy * width + sx) * 4;
destPixels[destIndex] = sourcePixels[sourceIndex];
destPixels[destIndex + 1] = sourcePixels[sourceIndex + 1];
destPixels[destIndex + 2] = sourcePixels[sourceIndex + 2];
destPixels[destIndex + 3] = sourcePixels[sourceIndex + 3];
}
}
}
ctx.putImageData(destData, 0, 0);
};
/**
* VISUAL PITCH SHIFT: Shifts the hue of the image, like changing the pitch of a voice.
*/
const applyPitchShift = () => {
const hueAngle = (clampedIntensity - 0.5) * 360; // range from -180 to 180
ctx.filter = `hue-rotate(${hueAngle}deg)`;
ctx.drawImage(originalImg, 0, 0);
ctx.filter = 'none';
};
/**
* VISUAL ROBOT: Pixelates the image, like a robotic, bit-crushed voice.
*/
const applyRobot = () => {
const size = (1.0 - clampedIntensity) * 0.2 + 0.01; // Scale factor, avoid 0
const w = width * size;
const h = height * size;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
ctx.imageSmoothingEnabled = true;
};
/**
* VISUAL FLANGER: Creates a chromatic aberration effect by shifting color channels.
*/
const applyFlanger = () => {
ctx.drawImage(originalImg, 0, 0);
const sourceData = ctx.getImageData(0, 0, width, height);
const destData = ctx.createImageData(width, height);
const sourcePixels = sourceData.data;
const destPixels = destData.data;
const offset = Math.round(width * 0.05 * clampedIntensity);
const getIndex = (x, y) => (y * width + Math.max(0, Math.min(x, width - 1))) * 4;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = (y * width + x) * 4;
const rIndex = getIndex(x - offset, y);
const gIndex = getIndex(x, y);
const bIndex = getIndex(x + offset, y);
destPixels[i] = sourcePixels[rIndex]; // Red from left
destPixels[i + 1] = sourcePixels[gIndex + 1]; // Green from center
destPixels[i + 2] = sourcePixels[bIndex + 2]; // Blue from right
destPixels[i + 3] = sourcePixels[gIndex + 3]; // Alpha from center
}
}
ctx.putImageData(destData, 0, 0);
};
// --- Main Logic ---
// Apply the selected effect
switch (effectName.toLowerCase()) {
case 'echo':
applyEcho();
break;
case 'reverb':
applyReverb();
break;
case 'distortion':
applyDistortion();
break;
case 'pitch':
applyPitchShift();
break;
case 'robot':
applyRobot();
break;
case 'flanger':
applyFlanger();
break;
case 'none':
default:
ctx.drawImage(originalImg, 0, 0);
break;
}
return canvas;
}
Apply Changes