You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, intensity = 1.0, mood = "magical") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth || originalImg.width;
const h = originalImg.naturalHeight || originalImg.height;
canvas.width = w;
canvas.height = h;
// Clamp intensity to a reasonable range (e.g., 0.0 to 2.0)
// 0.0 means minimal/no effect, 1.0 is default, 2.0 is very strong.
const clampedIntensity = Math.max(0.0, Math.min(2.0, intensity));
// Calculate filter parameters based on intensity
// Saturation: Normal 100%. Target for full intensity (2.0) around 180-200%.
// Default intensity (1.0) -> 100 + (180-100)/2 * 1.0 = 140%
const baseSaturation = 100;
const targetMaxSaturation = 180; // Max saturation at clampedIntensity = 2.0
const saturation = baseSaturation + (targetMaxSaturation - baseSaturation) / 2 * clampedIntensity;
// Contrast: Normal 100%. Target for full intensity (2.0) around 130-140%.
// Default intensity (1.0) -> 100 + (130-100)/2 * 1.0 = 115%
const baseContrast = 100;
const targetMaxContrast = 130;
const contrast = baseContrast + (targetMaxContrast - baseContrast) / 2 * clampedIntensity;
// Brightness: Normal 100%. Target for full intensity (2.0) around 110-115%.
// Default intensity (1.0) -> 100 + (110-100)/2 * 1.0 = 105%
const baseBrightness = 100;
const targetMaxBrightness = 110;
const brightness = baseBrightness + (targetMaxBrightness - baseBrightness) / 2 * clampedIntensity;
// Blur: Max blur (e.g., 1px) at full intensity (2.0).
// Default intensity (1.0) -> 0.8px / 2 * 1.0 = 0.4px
const maxBlur = 0.8; // Max blur in pixels
const blurAmount = (maxBlur / 2) * clampedIntensity;
// Hue Rotation: Max hue rotation (e.g., 10deg) at full intensity (2.0).
// Default intensity (1.0) -> 10 A_MAX_HUE_ROTATION / 2 * 1.0 = 5deg
const maxHueRotation = 10; // Max hue rotation in degrees
const hueRotation = (maxHueRotation / 2) * clampedIntensity;
// Build the filter string for canvas context
const filterConfig = [];
if (Math.abs(saturation - 100) > 0.1) filterConfig.push(`saturate(${saturation.toFixed(0)}%)`);
if (Math.abs(contrast - 100) > 0.1) filterConfig.push(`contrast(${contrast.toFixed(0)}%)`);
if (Math.abs(brightness - 100) > 0.1) filterConfig.push(`brightness(${brightness.toFixed(0)}%)`);
if (blurAmount > 0.05) filterConfig.push(`blur(${blurAmount.toFixed(2)}px)`); // Avoid very small blurs
if (Math.abs(hueRotation) > 0.1) filterConfig.push(`hue-rotate(${hueRotation.toFixed(0)}deg)`);
if (filterConfig.length > 0) {
ctx.filter = filterConfig.join(' ');
} else {
ctx.filter = 'none';
}
ctx.drawImage(originalImg, 0, 0, w, h);
// Clear filter before drawing overlays
ctx.filter = 'none';
// Vignette: Darkening at edges
if (clampedIntensity > 0.05) { // Apply vignette even for small intensities
const centerX = w / 2;
const centerY = h / 2;
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
// Inner radius of vignette (clear area)
// Becomes slightly smaller (more vignette) as intensity increases
const innerRadiusRatioStart = 0.5; // At intensity 0
const innerRadiusRatioEnd = 0.3; // At intensity 2.0
const currentInnerRatio = innerRadiusRatioStart - (innerRadiusRatioStart - innerRadiusRatioEnd) * (clampedIntensity / 2.0);
const innerRadius = outerRadius * currentInnerRatio;
const maxVignetteStrength = 0.6; // How dark edges become (0 to 1 alpha for black)
const currentVignetteStrength = (maxVignetteStrength / 2) * clampedIntensity;
const vignetteGradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
vignetteGradient.addColorStop(0, 'rgba(0,0,0,0)'); // Center is transparent
vignetteGradient.addColorStop(0.8, `rgba(0,0,0,${(currentVignetteStrength * 0.5).toFixed(3)})`); // Transition
vignetteGradient.addColorStop(1, `rgba(0,0,0,${currentVignetteStrength.toFixed(3)})`); // Edge darkness
ctx.fillStyle = vignetteGradient;
ctx.globalCompositeOperation = 'source-over'; // Default, but explicit
ctx.fillRect(0, 0, w, h);
}
// Color tint overlay based on mood
if (clampedIntensity > 0.1) {
// Max opacity for the tint is e.g. 0.12 at intensity 2.0.
// At default intensity 1.0, opacity is 0.06.
const maxTintOpacity = 0.12;
const tintOpacity = (maxTintOpacity / 2 * clampedIntensity).toFixed(3);
let tintColor = '';
switch (mood.toLowerCase()) {
case "fairy": // Warm, slightly pinkish/golden
tintColor = `rgba(255, 220, 230, ${tintOpacity})`; // Soft warm pink
break;
case "dreamy": // Soft, perhaps slightly desaturated, neutral warmth
tintColor = `rgba(230, 225, 235, ${tintOpacity})`; // Very light, almost neutral lavender/warm gray
break;
case "magical": // Cool, bluish, common for "magic"
default:
tintColor = `rgba(200, 220, 255, ${tintOpacity})`; // Light cool blue
break;
}
if (tintColor) {
ctx.globalCompositeOperation = 'soft-light';
ctx.fillStyle = tintColor;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation
}
}
return canvas;
}
Apply Changes