You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, grainStrength = 20, vignetteIntensity = 0.6, warmth = 0.15, desaturation = 0.25, contrast = 1.15, brightness = 0.98, slightBlur = 0.3) {
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;
// 1. Apply global filters using ctx.filter for blur, contrast, brightness, saturation
let filterString = '';
if (slightBlur > 0) {
filterString += `blur(${slightBlur}px) `;
}
// Adjust contrast and brightness
filterString += `contrast(${contrast}) brightness(${brightness}) `;
// Apply desaturation. Example: if desaturation is 0.25, saturation becomes 1 - 0.25 = 0.75 (75%).
filterString += `saturate(${1 - desaturation}) `;
if (filterString.trim() !== '') {
ctx.filter = filterString.trim();
}
// Draw the image with the applied global filters
ctx.drawImage(originalImg, 0, 0, w, h);
// Reset filters for subsequent manual pixel manipulations if any, or for drawing overlays
ctx.filter = 'none';
// Get image data for pixel manipulation
// This is done after initial filter applications to build upon them
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
const len = data.length;
// 2. Apply warmth (custom pixel manipulation for a film-like color cast)
// This simulates a warming filter or aged film stock characteristics.
if (warmth > 0) {
for (let i = 0; i < len; i += 4) {
// Skip fully transparent pixels
if (data[i + 3] === 0) continue;
// Boost red and green channels, optionally slightly reduce blue
data[i] = Math.min(255, data[i] * (1 + warmth * 0.6) + 15 * warmth); // Red
data[i + 1] = Math.min(255, data[i + 1] * (1 + warmth * 0.3) + 8 * warmth); // Green
// Example for reducing blue slightly: data[i + 2] = Math.max(0, data[i + 2] * (1 - warmth * 0.1));
}
}
// 3. Add Film Grain
if (grainStrength > 0) {
for (let i = 0; i < len; i += 4) {
// Skip fully transparent pixels
if (data[i + 3] === 0) continue;
// Add random noise to R, G, B channels
const noise = (Math.random() - 0.5) * grainStrength;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise));
}
}
// Put the modified pixel data (warmth and grain) back onto the canvas
if (warmth > 0 || grainStrength > 0) {
ctx.putImageData(imageData, 0, 0);
}
// 4. Add Vignetting
// This is applied last, as an overlay on the already processed image.
if (vignetteIntensity > 0) {
ctx.save(); // Save current canvas state
// Use 'source-over' to draw a semi-transparent gradient on top
ctx.globalCompositeOperation = 'source-over';
const centerX = w / 2;
const centerY = h / 2;
// Outer radius should cover the entire canvas, reaching the corners
const outerRadius = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
// Inner radius of the vignette effect, controlled by intensity.
// Higher intensity means a smaller, more focused transparent center.
const innerRadiusRatio = Math.max(0.1, 0.6 - vignetteIntensity * 0.5);
const innerRadius = outerRadius * innerRadiusRatio;
const gradient = ctx.createRadialGradient(
centerX, centerY, innerRadius, // Inner circle (start of gradient)
centerX, centerY, outerRadius // Outer circle (end of gradient)
);
// Define how dark the vignette edges are
const vignetteDarkness = Math.min(1, vignetteIntensity * 1.1); // Multiplier can be adjusted
// Gradient stops: from transparent center to dark edges
gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Center is fully transparent
gradient.addColorStop(0.7, `rgba(0,0,0,${vignetteDarkness * 0.6})`); // Mid-point of vignette transition
gradient.addColorStop(1, `rgba(0,0,0,${vignetteDarkness})`); // Edges are dark
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h); // Apply the vignette gradient over the entire canvas
ctx.restore(); // Restore canvas state to before vignette drawing
}
return canvas;
}
Apply Changes