You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
saturationFactor = 0.8, // Range: 0.0 (grayscale) to 2.0 (super vibrant). 1.0 is original. Default: 0.8 (slightly desaturated)
contrastFactor = 1.2, // Range: 0.0 to 3.0. 1.0 is original. Default: 1.2 (increased contrast)
warmthFactor = 0.05, // Range: -0.3 (cool) to 0.3 (warm). 0.0 is no change. Default: 0.05 (slight warmth)
vignetteStrength = 0.5, // Range: 0.0 (none) to 1.0 (strong). Opacity of vignette at edges. Default: 0.5
grainStrength = 15, // Range: 0 (none) to 50 (heavy). Intensity of film grain. Default: 15 (subtle grain)
fadeAmount = 5 // Range: 0 (none) to 50 (very faded). Lifts black levels. Default: 5 (slight fade)
) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can sometimes optimize frequent getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// Get image data for pixel manipulation
const imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
const data = imageData.data;
const length = data.length;
const contrastMidPoint = 128; // Mid-point for contrast adjustment
// Process each pixel
for (let i = 0; i < length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Saturation adjustment
if (saturationFactor !== 1.0) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Calculate luminance
r = gray + (r - gray) * saturationFactor;
g = gray + (g - gray) * saturationFactor;
b = gray + (b - gray) * saturationFactor;
}
// 2. Warmth/Cooling adjustment
if (warmthFactor !== 0) {
// Positive warmthFactor makes it warmer, negative makes it cooler
r = r * (1 + warmthFactor);
g = g * (1 + warmthFactor * 0.5); // Green is affected less for a balanced tint
b = b * (1 - warmthFactor);
}
// 3. Contrast adjustment
if (contrastFactor !== 1.0) {
r = contrastMidPoint + (r - contrastMidPoint) * contrastFactor;
g = contrastMidPoint + (g - contrastMidPoint) * contrastFactor;
b = contrastMidPoint + (b - contrastMidPoint) * contrastFactor;
}
// 4. Fade (Lift Blacks) adjustment
if (fadeAmount > 0) {
r += fadeAmount;
g += fadeAmount;
b += fadeAmount;
}
// 5. Film Grain adjustment
if (grainStrength > 0) {
// Add monochrome noise
const noise = (Math.random() - 0.5) * grainStrength;
r += noise;
g += noise;
b += noise;
}
// Clamp values to the valid [0, 255] range
data[i] = Math.max(0, Math.min(255, r));
data[i + 1] = Math.max(0, Math.min(255, g));
data[i + 2] = Math.max(0, Math.min(255, b));
// Alpha channel (data[i+3]) is preserved
}
// Put the modified image data back onto the canvas
ctx.putImageData(imageData, 0, 0);
// 6. Vignette effect
if (vignetteStrength > 0) {
const centerX = imgWidth / 2;
const centerY = imgHeight / 2;
// Outer radius to cover image corners
const outerRadius = Math.hypot(centerX, centerY);
// Inner radius ratio defines the size of the clear central area
const innerRadiusRatio = 0.25;
const gradient = ctx.createRadialGradient(
centerX, centerY, outerRadius * innerRadiusRatio,
centerX, centerY, outerRadius
);
// Vignette darkens edges, so use black with varying alpha
gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Transparent center
// Add a mid-stop for smoother falloff
gradient.addColorStop(0.6, `rgba(0,0,0,${vignetteStrength * 0.35})`);
gradient.addColorStop(1, `rgba(0,0,0,${vignetteStrength})`); // Darker edge
ctx.fillStyle = gradient;
// Default 'source-over' compositing draws the semi-transparent black on top
ctx.fillRect(0, 0, imgWidth, imgHeight);
}
return canvas;
}
Apply Changes