You can edit the below JavaScript code to customize the image tool.
async function processImage(
originalImg,
desaturationLevel = 0.2,
contrastReduction = 0.15,
coolTintStrength = 20,
grainAmount = 10
) {
// Clamp parameters to sensible ranges
desaturationLevel = Math.max(0, Math.min(1, desaturationLevel));
// For contrastReduction, a value of 1 makes the image entirely mid-gray.
contrastReduction = Math.max(0, Math.min(1, contrastReduction));
coolTintStrength = Math.max(0, coolTintStrength); // Must be non-negative
grainAmount = Math.max(0, grainAmount); // Must be non-negative
const canvas = document.createElement('canvas');
// Set willReadFrequently for potential performance improvement on repeated 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;
// Handle cases where image might not be loaded or has zero dimensions
if (canvas.width === 0 || canvas.height === 0) {
console.warn("Image has zero dimensions. Ensure it's loaded before processing.");
// Ensure canvas has at least 1x1 dimension for drawing fallback
canvas.width = Math.max(1, canvas.width);
canvas.height = Math.max(1, canvas.height);
// Draw a simple placeholder/error message
ctx.fillStyle = 'lightgray';
ctx.fillRect(0,0,canvas.width, canvas.height);
if (typeof ctx.fillText === 'function') {
ctx.fillStyle = 'red';
ctx.font = '10px Arial'; // Small font for tiny canvas
if (canvas.width > 50 && canvas.height > 20) { // Slightly larger font for larger placeholder
ctx.font = `${Math.min(canvas.width/4, canvas.height/2, 20)}px Arial`;
}
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Error', canvas.width/2, canvas.height/2);
}
return canvas;
}
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
console.error("Could not get image data, possibly due to cross-origin restrictions:", e);
// Fallback: return canvas with original image drawn and an error message overlay.
// The original image is already drawn on the canvas.
if (typeof ctx.fillText === 'function') {
ctx.fillStyle = "rgba(255, 0, 0, 0.7)"; // Semi-transparent red
ctx.textAlign = "center";
ctx.textBaseline = 'middle';
const fontSize = Math.max(12, Math.min(canvas.width, canvas.height) / 10); // Responsive font size
ctx.font = `bold ${fontSize}px Arial`;
// Simple text shadow for better visibility
ctx.shadowColor = "black";
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.shadowBlur = 3;
ctx.fillText("Processing Error", canvas.width / 2, canvas.height / 2);
// Reset shadow properties for any subsequent drawing (if any)
ctx.shadowColor = "transparent";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
}
return canvas; // Return the canvas with the original image and error text
}
const data = imageData.data;
// Helper to clamp color values to the 0-255 range
const clamp = (value) => Math.max(0, Math.min(255, value));
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// Alpha channel (data[i+3]) is preserved throughout
// 1. Desaturation
// Reduces color saturation, moving colors towards grayscale.
if (desaturationLevel > 0) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Standard luminance calculation
r = r * (1 - desaturationLevel) + gray * desaturationLevel;
g = g * (1 - desaturationLevel) + gray * desaturationLevel;
b = b * (1 - desaturationLevel) + gray * desaturationLevel;
}
// 2. Contrast Reduction
// Flattens the image by pulling color values towards mid-gray (128).
if (contrastReduction > 0) {
r = r * (1 - contrastReduction) + 128 * contrastReduction;
g = g * (1 - contrastReduction) + 128 * contrastReduction;
b = b * (1 - contrastReduction) + 128 * contrastReduction;
}
// 3. Cool Tint (applied more strongly to shadows and midtones)
// Adds a blue/cyan cast, more pronounced in darker areas.
if (coolTintStrength > 0) {
// Calculate luminance from current (potentially modified) r,g,b values
const currentLuminance = (r + g + b) / 3;
// lumFactor: 1 for black (max tint), 0 for white (no tint). Clamped to be non-negative.
const lumFactor = Math.max(0, (255 - currentLuminance) / 255);
b += coolTintStrength * lumFactor; // Add blue
g += coolTintStrength * 0.6 * lumFactor; // Add some green for a cyan/cooler tone
}
// Clamp intermediate values after tinting, as tinting might push them out of range.
// r is not directly changed by tinting logic above, but good practice to include if logic changes.
r = clamp(r);
g = clamp(g);
b = clamp(b);
// 4. Film Grain
// Adds random noise to simulate film grain.
if (grainAmount > 0) {
// Generates noise values typically in the range [-grainAmount/2, grainAmount/2]
const noise = (Math.random() - 0.5) * grainAmount;
r += noise;
g += noise;
b += noise;
}
// Final assignment: clamp modified RGB values and round to nearest integer.
data[i] = Math.round(clamp(r));
data[i + 1] = Math.round(clamp(g));
data[i + 2] = Math.round(clamp(b));
}
ctx.putImageData(imageData, 0, 0);
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 Pull-Processed Film Filter Effect Tool allows users to apply a creative filter effect to their images by adjusting various parameters. This tool can desaturate colors, reduce contrast, add a cool tint, and simulate film grain, resulting in a nostalgic or artistic look reminiscent of processed film photography. Ideal for photographers, graphic designers, and social media enthusiasts, this tool enhances images for creative projects, personal sharing, or artistic expression.