You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
saturation = 1.4, // Example: 0 (grayscale) to 2+ (amped saturation), 1 is original
blurPx = 0.5, // Example: 0 (no blur) to N pixels for Gaussian blur
brightness = 1.05, // Example: 0 (black) to 2+ (amped brightness), 1 is original
contrast = 1.05, // Example: 0 (gray) to 2+ (amped contrast), 1 is original
tintColorStr = "255,200,150", // RGB string for tint, e.g., "255,200,150" for warm peach
tintOpacity = 0.15, // 0 (no tint) to 1 (full tint layer opacity)
vignetteColorStr = "40,20,10", // RGB string for vignette, e.g., "40,20,10" for dark warm brown
vignetteStrength = 0.6 // 0 (no vignette) to 1 (strongest vignette: color at full opacity from image center)
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { alpha: true }); // Ensure alpha for transparency
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
console.error("Image has zero dimensions. Ensure the image is loaded and valid.");
// Return a small, transparent canvas as a fallback
canvas.width = 1;
canvas.height = 1;
ctx.clearRect(0,0,1,1);
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
// 1. Apply base image filters (saturation, blur, brightness, contrast) and draw the image
let filterString = '';
// Saturate: values < 1 desaturate, 1 is original, > 1 supersaturates
if (saturation !== 1) filterString += `saturate(${Math.max(0, saturation)}) `;
// Blur: px value for Gaussian blur. Must be non-negative.
if (blurPx > 0) filterString += `blur(${Math.max(0, blurPx)}px) `;
// Brightness: 0 is black, 1 is original, > 1 is brighter
if (brightness !== 1) filterString += `brightness(${Math.max(0, brightness)}) `;
// Contrast: 0 is solid gray, 1 is original, > 1 is higher contrast
if (contrast !== 1) filterString += `contrast(${Math.max(0, contrast)}) `;
if (filterString.trim() !== '') {
ctx.filter = filterString.trim();
}
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
ctx.filter = 'none'; // Reset filter: subsequent drawing operations are not affected
// 2. Apply color tint overlay
// tintOpacity: 0 (no tint) to 1 (full tint effect)
// tintColorStr: "R,G,B" string
const clampedTintOpacity = Math.max(0, Math.min(1, tintOpacity));
if (clampedTintOpacity > 0 && tintColorStr && tintColorStr.trim() !== "") {
const parsedColor_tint = tintColorStr.split(',').map(s => parseInt(s.trim(), 10));
if (parsedColor_tint.length === 3 && parsedColor_tint.every(num => !isNaN(num) && num >= 0 && num <= 255)) {
const [r, g, b] = parsedColor_tint;
// 'overlay' blending mode often gives a nice, rich tint effect.
// 'soft-light' is another option for a more subtle effect.
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = `rgba(${r},${g},${b},${clampedTintOpacity})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'source-over'; // Reset to default blending mode
} else {
console.warn(`Invalid tintColorStr: "${tintColorStr}". Expected format "R,G,B" with values 0-255.`);
}
}
// 3. Apply vignette effect
// vignetteStrength: 0 (no vignette) to 1 (strongest vignette). Controls both spread and opacity.
// vignetteColorStr: "R,G,B" string for the vignette color.
const clampedVignetteStrength = Math.max(0, Math.min(1, vignetteStrength)); // Clamp strength [0,1]
if (clampedVignetteStrength > 0 && vignetteColorStr && vignetteColorStr.trim() !== "") {
const parsedColor_vignette = vignetteColorStr.split(',').map(s => parseInt(s.trim(), 10));
if (parsedColor_vignette.length === 3 && parsedColor_vignette.every(num => !isNaN(num) && num >= 0 && num <= 255)) {
const [vr, vg, vb] = parsedColor_vignette;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// The radial gradient defines how the vignette color fades in from the center.
// r0: Radius of the central area where the vignette is fully transparent.
// r1: Radius where the vignette reaches its target color and opacity (usually image edges).
const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
// vignetteStrength determines how far the clear area (r0) extends.
// strength = 0: r0 = maxRadius (fully clear).
// strength = 1: r0 = 0 (effect starts from center).
const r0 = maxRadius * (1 - clampedVignetteStrength);
const r1 = maxRadius;
if (r0 < r1) { // Ensure r0 is less than r1 for a valid gradient.
const gradient = ctx.createRadialGradient(centerX, centerY, r0, centerX, centerY, r1);
// Start of the gradient (at r0): vignette color, but fully transparent.
gradient.addColorStop(0, `rgba(${vr},${vg},${vb},0)`);
// End of the gradient (at r1): vignette color with opacity determined by vignetteStrength.
gradient.addColorStop(1, `rgba(${vr},${vg},${vb},${clampedVignetteStrength})`);
// Apply the gradient fill. Default 'source-over' composite op will overlay this.
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// If r0 >= r1 (e.g., strength is 0 or very near 0), no visible vignette is drawn, which is correct.
} else {
console.warn(`Invalid vignetteColorStr: "${vignetteColorStr}". Expected format "R,G,B" with values 0-255.`);
}
}
return canvas;
}
Apply Changes