You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, applyMonochrome = 0, saturation = 0.85, contrast = 1.2, brightness = 0.98, vignetteStrength = 0.35, grainAmount = 0.08) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// 1. Apply initial filters (Brightness, Contrast, Monochrome/Saturation)
// These CSS-like filters are hardware-accelerated in most browsers.
// They are applied in the order they appear in the filter string.
let filterOps = [];
filterOps.push(`brightness(${brightness})`); // Adjusts overall lightness. 1 is original.
filterOps.push(`contrast(${contrast})`); // Adjusts contrast. 1 is original.
if (applyMonochrome === 1) {
filterOps.push('grayscale(1)'); // Converts image to monochrome. 1 means fully grayscale.
} else {
// Saturation is only applied if not monochrome.
// 1 is original saturation, 0 is grayscale, >1 increases saturation.
filterOps.push(`saturate(${saturation})`);
}
if (filterOps.length > 0) {
ctx.filter = filterOps.join(' ');
}
// Draw the original image onto the canvas, applying the stacked filters
ctx.drawImage(originalImg, 0, 0, w, h);
// Reset all filters for subsequent manual operations (vignette, grain)
// as ctx.filter would affect fillRect and putImageData otherwise.
ctx.filter = 'none';
// 2. Apply Vignette
// vignetteStrength: 0 = no vignette, 1 = fully dark (black) vignette at edges.
// This effect darkens the corners of the image.
if (vignetteStrength > 0 && vignetteStrength <= 1) {
ctx.save(); // Save context state (important for globalCompositeOperation and fillStyle)
const centerX = w / 2;
const centerY = h / 2;
// Calculate the radius to the furthest corner of the canvas to ensure full coverage
const outerRadius = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
// Define where the vignette effect starts to fade in, as a ratio of the outerRadius.
// e.g., 0.3 means the vignette begins its fade 30% of the way from the center to the edge.
const vignetteInnerStop = 0.3;
const gradient = ctx.createRadialGradient(
centerX, centerY, outerRadius * vignetteInnerStop, // Inner circle (start of gradient)
centerX, centerY, outerRadius // Outer circle (end of gradient)
);
// For 'multiply' blend mode:
// The center of the gradient (inner circle) should be white (rgb(255,255,255)).
// Multiplying by white (equivalent to 1.0) causes no change to the underlying image pixels.
gradient.addColorStop(0, 'rgba(255,255,255,1)');
// The edge of the gradient (outer circle) determines the amount of darkening.
// vignetteStrength = 0 -> edge color is white (no darkening effect).
// vignetteStrength = 1 -> edge color is black (maximum darkening effect).
// The color value for RGB channels is (1 - vignetteStrength) * 255.
const edgeDarknessAmount = Math.max(0, Math.min(1, 1 - vignetteStrength)); // Clamp between 0 and 1
const edgeColorVal = Math.floor(edgeDarknessAmount * 255);
gradient.addColorStop(1, `rgba(${edgeColorVal},${edgeColorVal},${edgeColorVal},1)`);
ctx.globalCompositeOperation = 'multiply'; // Pixels from gradient multiply with canvas pixels
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h); // Apply gradient over the whole canvas
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation to default
ctx.restore(); // Restore context state
}
// 3. Apply Film Grain
// grainAmount: 0 = no grain, 1 = very strong grain.
// This adds random noise to pixel values to simulate film grain.
if (grainAmount > 0 && grainAmount <= 1) {
// This operation can be performance-intensive on large images.
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data; // Pixel data: [R,G,B,A, R,G,B,A, ...]
// Adjust the intensity of the grain effect.
// If grainAmount = 0.08, grainEffectStrength = 6.4. Noise range is approx. +/-3.2.
// If grainAmount = 1, grainEffectStrength = 80. Noise range is approx. +/-40.
const grainEffectStrength = grainAmount * 80;
for (let i = 0; i < data.length; i += 4) {
// Add the same random noise value to R, G, B for monochrome (grayscale) grain
// Math.random() - 0.5 gives a range of [-0.5, 0.5)
const noise = (Math.random() - 0.5) * grainEffectStrength;
data[i] = Math.max(0, Math.min(255, data[i] + noise)); // Red channel
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise)); // Green channel
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise)); // Blue channel
// Alpha channel (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0); // Write the modified pixel data back to the canvas
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Leica M6 Camera Render Filter Effect tool allows users to apply a film-inspired aesthetic to their images, reminiscent of photographs taken with a Leica M6 camera. This tool provides customization options including brightness, contrast, monochrome conversion, saturation, vignette strength, and film grain effects. Users can enhance their photographs by simulating classic film characteristics, making it suitable for photographers, graphic designers, and social media enthusiasts looking to add a vintage touch to their visuals.