You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, grainAmountInput = 20, vignetteIntensityInput = 0.6) {
// Validate and sanitize input parameters
let grainAmount = Number(grainAmountInput);
if (isNaN(grainAmount) || grainAmount < 0) {
grainAmount = 20; // Default value if input is invalid or negative
}
let vignetteIntensity = Number(vignetteIntensityInput);
if (isNaN(vignetteIntensity)) {
vignetteIntensity = 0.6; // Default value if input is NaN
}
// Clamp vignetteIntensity to the logical range [0, 1]
vignetteIntensity = Math.max(0, Math.min(1, vignetteIntensity));
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Get image data to manipulate pixels
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const height = canvas.height;
// Fixed effect parameters for the "Argus C3 Vintage" look
const desaturationAmount = 0.15; // Level of desaturation
const colorBoostR = 1.05; // Factor to boost red channel
const colorBoostG = 1.02; // Factor to boost green channel
const colorReductionB = 0.90; // Factor to reduce blue channel
const warmthAddR = 5; // Value to add to red channel for warmth
const brightnessAdjustment = -5; // Overall brightness change
const contrastLevel = 25; // Defines contrast enhancement (approx -100 to 100)
// Pre-calculate contrast factor
const contrastFactor = (259 * (contrastLevel + 255)) / (255 * (259 - contrastLevel));
// Process each pixel
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Desaturation
const grayVal = 0.299 * r + 0.587 * g + 0.114 * b;
r = r * (1 - desaturationAmount) + grayVal * desaturationAmount;
g = g * (1 - desaturationAmount) + grayVal * desaturationAmount;
b = b * (1 - desaturationAmount) + grayVal * desaturationAmount;
// 2. Color Aging/Shift (simulating aged film with a yellowish tint)
r = r * colorBoostR + warmthAddR;
g = g * colorBoostG;
b = b * colorReductionB;
// 3. Brightness Adjustment
r += brightnessAdjustment;
g += brightnessAdjustment;
b += brightnessAdjustment;
// 4. Contrast Enhancement
r = contrastFactor * (r - 128) + 128;
g = contrastFactor * (g - 128) + 128;
b = contrastFactor * (b - 128) + 128;
// 5. Noise (Film Grain) - Controlled by grainAmount parameter
if (grainAmount > 0) {
const noise = (Math.random() - 0.5) * grainAmount;
r += noise;
g += noise;
b += noise;
}
// Clamp values to the 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]) remains unchanged
}
// Put the modified pixel data back onto the canvas
ctx.putImageData(imageData, 0, 0);
// 6. Vignetting - Controlled by vignetteIntensity parameter
if (vignetteIntensity > 0) {
const centerX = width / 2;
const centerY = height / 2;
// Distance from center to a corner, defines max radius for vignette
const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);
// Vignette appearance parameters (fixed for this filter style)
const vignetteSpread = 0.75; // How far vignette extends inwards (0=edges only, 1=reaches center)
const vignetteEdgeColorR = 40; // Base color for dark vignette edge (dark warm brown)
const vignetteEdgeColorG = 20;
const vignetteEdgeColorB = 5;
const rOuter = maxDist; // Vignette fully opaque at this radius
const rInner = maxDist * (1 - vignetteSpread); // Vignette starts becoming visible from this radius
const gradient = ctx.createRadialGradient(centerX, centerY, rInner, centerX, centerY, rOuter);
// Calculate the actual color for the vignette's edge based on intensity.
// For 'multiply' blend mode, the vignette layer color determines how much original pixels are darkened.
// A white color (255,255,255) in the vignette layer results in no change.
// A darker color results in darkening.
// vignetteIntensity = 0 -> edge is white (no effect)
// vignetteIntensity = 1 -> edge is (vignetteEdgeColorR, G, B)
const finalEdgeR = (1 - vignetteIntensity) * 255 + vignetteIntensity * vignetteEdgeColorR;
const finalEdgeG = (1 - vignetteIntensity) * 255 + vignetteIntensity * vignetteEdgeColorG;
const finalEdgeB = (1 - vignetteIntensity) * 255 + vignetteIntensity * vignetteEdgeColorB;
gradient.addColorStop(0, 'rgba(255,255,255,1)'); // Center of gradient: white (no effect with multiply)
gradient.addColorStop(1, `rgba(${finalEdgeR},${finalEdgeG},${finalEdgeB},1)`); // Edge of gradient: calculated color
ctx.fillStyle = gradient;
ctx.globalCompositeOperation = 'multiply'; // Apply vignette by darkening existing pixels
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation to default
}
return canvas;
}
Apply Changes