You can edit the below JavaScript code to customize the image tool.
function processImage(
originalImg,
toneParam = "sepia", // "sepia", "grayscale", or "none" for no color tone change
brightnessOffsetParam = 0, // Adjusts brightness. Suggested range: -50 to 50. Default is 0 (no change).
contrastFactorParam = 1.5, // Adjusts contrast. 1.0 is no change. Suggested range: 0.5 to 3.0.
grainAmountParam = 25, // Adds film grain. Suggested range: 0 (no grain) to 50.
vignetteStrengthParam = 0.7 // Adds vignette. Range: 0.0 (no vignette) to 1.0 (strong dark corners).
) {
// Parameter validation and normalization
let tone = typeof toneParam === 'string' ? String(toneParam).toLowerCase() : "sepia";
if (tone !== "sepia" && tone !== "grayscale" && tone !== "none") {
tone = "sepia"; // Default fallback if an unrecognized string is provided
}
let brightnessOffset = Number(brightnessOffsetParam);
if (isNaN(brightnessOffset)) {
brightnessOffset = 0; // Default if Not-a-Number
}
let contrastFactor = Number(contrastFactorParam);
// contrastFactor must be non-negative. 1.0 means no change.
if (isNaN(contrastFactor) || contrastFactor < 0) {
contrastFactor = 1.5; // Default if NaN or negative
}
let grainAmount = Number(grainAmountParam);
if (isNaN(grainAmount) || grainAmount < 0) {
grainAmount = 25; // Default if NaN or negative
}
let vignetteStrength = Number(vignetteStrengthParam);
if (isNaN(vignetteStrength) || vignetteStrength < 0.0 || vignetteStrength > 1.0) {
vignetteStrength = 0.7; // Default if NaN or outside the 0.0-1.0 range
}
const canvas = document.createElement('canvas');
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
// Handle cases of unloaded or zero-size images
if (width === 0 || height === 0) {
canvas.width = 0;
canvas.height = 0;
console.warn("Image has zero width or height. Returning empty canvas.");
return canvas;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
if (!ctx) {
console.error("Could not get 2D rendering context. Returning empty canvas.");
// Fallback for environments where canvas context might fail
return canvas; // canvas is configured with width/height but will be blank
}
try {
ctx.drawImage(originalImg, 0, 0, width, height);
} catch (e) {
console.error("Error drawing image to canvas:", e);
// This might happen if originalImg is not a valid image source
return canvas; // Return canvas (blank or partially drawn if error occurred mid-draw)
}
let imageData;
try {
imageData = ctx.getImageData(0, 0, width, height);
} catch (e) {
console.error("Error getting imageData (likely due to cross-origin restrictions if image is from another domain without CORS headers):", e);
// If imageData cannot be accessed, pixel manipulation is not possible.
// Return the canvas with the original image drawn on it, without effects.
return canvas;
}
const data = imageData.data;
const centerX = width / 2;
const centerY = height / 2;
// Max distance from center to a corner. Used for vignette normalization.
// This is guaranteed to be > 0 because width/height are > 0 here.
const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Tone (Sepia, Grayscale, or None)
if (tone === "sepia") {
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 = Math.min(255, tr);
g = Math.min(255, tg);
b = Math.min(255, tb);
} else if (tone === "grayscale") {
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
r = g = b = gray;
}
// If tone is "none" or an unhandled value, original r,g,b values are used.
// 2. Brightness Adjustment
if (brightnessOffset !== 0) {
r += brightnessOffset;
g += brightnessOffset;
b += brightnessOffset;
}
// Clamp values after brightness adjustment
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 3. Contrast Adjustment
// Factor > 1 increases contrast, < 1 decreases. Formula adjusts around midpoint 128.
if (contrastFactor !== 1.0) {
r = (r - 128) * contrastFactor + 128;
g = (g - 128) * contrastFactor + 128;
b = (b - 128) * contrastFactor + 128;
}
// Clamp values after contrast adjustment
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 4. Vignette Effect
if (vignetteStrength > 0 && maxDist > 0) {
const x = (i / 4) % width; // Current pixel's x coordinate
const y = Math.floor((i / 4) / width); // Current pixel's y coordinate
const dx = x - centerX;
const dy = y - centerY;
const dist = Math.sqrt(dx * dx + dy * dy); // Distance from center
// normalizedDist is 0 at center, 1 at the furthest corner
const normalizedDist = dist / maxDist;
// vignetteMultiplier is 1 at center, falls off towards edges
// vignetteStrength controls how much it falls off (0=none, 1=edges are black)
let vignetteMultiplier = 1.0 - (normalizedDist * vignetteStrength);
vignetteMultiplier = Math.max(0, Math.min(1, vignetteMultiplier)); // Clamp to 0-1
r *= vignetteMultiplier;
g *= vignetteMultiplier;
b *= vignetteMultiplier;
}
// 5. Film Grain Effect
if (grainAmount > 0) {
// Add random noise value (-0.5 to 0.5) scaled by grainAmount
const grain = (Math.random() - 0.5) * grainAmount;
r += grain;
g += grain;
b += grain;
}
// Final clamping for all pixel modifications
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]) is preserved
}
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 Silent Film Filter Effect Tool allows you to transform your images with a vintage silent film aesthetic. Users can apply various effects such as sepia or grayscale tones, adjust brightness, enhance contrast, add film grain, and create a vignette effect to give images an old-time cinematic look. This tool is ideal for creatives looking to replicate the visual styles of classic films or for anyone wanting to add a nostalgic touch to their digital photos. Whether for social media posts, artistic projects, or personal memories, this tool provides versatile options to enhance the visual storytelling of your images.