You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, contrast = 1.15, saturation = 1.05, coolTone = 0.05, shadowLift = 10, vignetteStrength = 0.2, vignetteSoftness = 0.7) {
// Parse parameters to ensure they are numbers
contrast = parseFloat(contrast);
saturation = parseFloat(saturation);
coolTone = parseFloat(coolTone);
shadowLift = parseFloat(shadowLift);
vignetteStrength = parseFloat(vignetteStrength);
vignetteSoftness = parseFloat(vignetteSoftness);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Optimization hint
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
const centerX = width / 2;
const centerY = height / 2;
const maxDist = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2));
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Saturation
// Adjusts the intensity of colors.
// saturation = 1.0: no change
// saturation < 1.0: desaturate
// saturation > 1.0: saturate
if (saturation !== 1.0) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Calculate luminance
r = gray + saturation * (r - gray);
g = gray + saturation * (g - gray);
b = gray + saturation * (b - gray);
// Clamp values to [0, 255]
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 2. Contrast
// Adjusts the difference between light and dark areas.
// contrast = 1.0: no change
// contrast > 1.0: increase contrast
// contrast < 1.0: decrease contrast (not typical for this filter)
if (contrast !== 1.0) {
r = 128 + (r - 128) * contrast;
g = 128 + (g - 128) * contrast;
b = 128 + (b - 128) * contrast;
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 3. Cool Tone (Blue/Cyan Tint)
// Adds a cool cast, characteristic of some film stocks.
// coolTone = 0: no change
// coolTone > 0: applies cool tint (increases blue/green, slightly reduces red)
if (coolTone > 0) {
b = b * (1 + coolTone); // Increase blue
g = g * (1 + coolTone * 0.5); // Slightly increase green (for cyan effect)
r = r * (1 - coolTone * 0.3); // Slightly decrease red
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 4. Shadow Lift (Fade Effect)
// Lifts the darkest parts of the image, giving a faded film look.
// shadowLift = 0: no change
// shadowLift > 0: lifts black levels (e.g., 10-30 typical range)
if (shadowLift > 0) {
// This formula adds more to darker values, effect fades towards highlights
r = r + shadowLift * (1.0 - r / 255.0);
g = g + shadowLift * (1.0 - g / 255.0);
b = b + shadowLift * (1.0 - b / 255.0);
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
}
// 5. Vignette
// Darkens the corners/edges of the image.
// vignetteStrength = 0: no vignette
// vignetteStrength > 0: strength of darkening (0.1 to 1.0)
// vignetteSoftness: controls the falloff (0.1 sharp to 1.0 very soft)
if (vignetteStrength > 0 && maxDist > 0) {
const x_coord = (i / 4) % width;
const y_coord = Math.floor((i / 4) / width);
const dx = x_coord - centerX;
const dy = y_coord - centerY;
const dist = Math.sqrt(dx * dx + dy * dy);
const normalizedDist = Math.min(1.0, dist / maxDist); // Normalize distance, cap at 1.0
// vignetteSoftness (0.1 to 1.0) maps to power inversely.
// High softness (1.0) -> low power (e.g., 1) -> spread out vignette.
// Low softness (0.1) -> high power (e.g., 3.5) -> concentrated vignette.
let power = 1.5; // Default falloff power
if (vignetteSoftness > 0 && vignetteSoftness <= 1) {
power = 1 + (1 - vignetteSoftness) * 2.5;
}
const vignetteAmount = Math.pow(normalizedDist, power);
const reduction = Math.max(0, 1.0 - vignetteAmount * vignetteStrength);
r *= reduction;
g *= reduction;
b *= reduction;
// Final clamp after vignette will handle boundary conditions for r,g,b
}
// Assign processed and clamped values back to image data
data[i] = Math.max(0, Math.min(255, Math.round(r)));
data[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
data[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
// Alpha channel (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes