You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, sepiaIntensity = 0.3, grainAmount = 0.1, vignetteStrength = 0.5, vignetteStart = 0.3, vignetteEnd = 0.95) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can be a performance hint for some browsers/operations.
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const w = originalImg.naturalWidth || originalImg.width;
const h = originalImg.naturalHeight || originalImg.height;
canvas.width = w;
canvas.height = h;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// Clamp input parameters to sensible ranges
const effectiveSepiaIntensity = Math.max(0, Math.min(1, sepiaIntensity));
const effectiveGrainAmount = Math.max(0, Math.min(0.5, grainAmount)); // Cap grain amount
const effectiveVignetteStrength = Math.max(0, Math.min(1, vignetteStrength));
// Apply Sepia and Grain pixel by pixel
if (effectiveSepiaIntensity > 0 || effectiveGrainAmount > 0) {
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// Apply Sepia
if (effectiveSepiaIntensity > 0) {
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
r = r * (1 - effectiveSepiaIntensity) + tr * effectiveSepiaIntensity;
g = g * (1 - effectiveSepiaIntensity) + tg * effectiveSepiaIntensity;
b = b * (1 - effectiveSepiaIntensity) + tb * effectiveSepiaIntensity;
}
// Apply Grain
if (effectiveGrainAmount > 0) {
// Monochrome grain: add the same random value to R, G, B
// The range of Math.random() - 0.5 is [-0.5, 0.5].
// Scaled by a factor (e.g., 100) and effectiveGrainAmount.
// Max grain magnitude is 50 at effectiveGrainAmount = 0.5.
const grain = (Math.random() - 0.5) * (100 * effectiveGrainAmount);
r += grain;
g += grain;
b += grain;
}
// Clamp pixel values to 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));
}
ctx.putImageData(imageData, 0, 0);
}
// Apply Vignette
if (effectiveVignetteStrength > 0) {
ctx.save();
// Using source-over to draw the vignette on top
ctx.globalCompositeOperation = 'source-over';
const centerX = w / 2;
const centerY = h / 2;
// The maximum radius is from the center to a corner
const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
// Validate and normalize vignetteStart and vignetteEnd
let normVignetteStart = Math.max(0, Math.min(1, vignetteStart));
let normVignetteEnd = Math.max(0, Math.min(1, vignetteEnd));
// Ensure vignetteEnd is greater than vignetteStart
if (normVignetteEnd <= normVignetteStart) {
// If start is at or near 1, push end to 1. Otherwise, make a small gap.
if (normVignetteStart >= 0.99) {
normVignetteEnd = 1;
normVignetteStart = Math.min(normVignetteStart, 0.99); // Ensure start is not 1 if end becomes 1
} else {
normVignetteEnd = normVignetteStart + 0.01;
}
}
// Ensure normVignetteEnd itself does not exceed 1 after adjustment
normVignetteEnd = Math.min(1, normVignetteEnd);
// Ensure start is less than end after all adjustments
if (normVignetteStart >= normVignetteEnd) {
normVignetteStart = Math.max(0, normVignetteEnd-0.01);
}
const startRadius = maxRadius * normVignetteStart;
const endRadius = maxRadius * normVignetteEnd;
if (startRadius >= endRadius) { // Failsafe if radii are invalid for gradient
if (effectiveVignetteStrength > 0) { // Still draw a full overlay if strength is there
ctx.fillStyle = `rgba(0,0,0,${effectiveVignetteStrength * 0.1})`; // very subtle full overlay as error state
ctx.fillRect(0,0,w,h);
}
} else {
const gradient = ctx.createRadialGradient(
centerX, centerY, startRadius, // Inner circle (for color stop 0)
centerX, centerY, endRadius // Outer circle (for color stop 1)
);
// Color stop 0: transparent black at startRadius
gradient.addColorStop(0, 'rgba(0,0,0,0)');
// Color stop 1: semi-transparent black at endRadius, strength controlled by vignetteStrength
gradient.addColorStop(1, `rgba(0,0,0,${effectiveVignetteStrength})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h); // Apply gradient over the entire canvas
}
ctx.restore();
}
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 35mm Film Filter Effect Tool allows users to apply a vintage look to their images, reminiscent of 35mm film photography. By adjusting parameters such as sepia intensity, grain amount, and vignette strength, users can create images with rich, warm tones, subtle noise, and a gradient darkening around the edges. This tool is ideal for photographers, graphic designers, and social media enthusiasts looking to enhance their images with a nostalgic aesthetic.