You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, romanticFactor = 0.7, softness = 1.0, vignetteIntensity = 0.5) {
// Validate and clamp parameters. parseFloat ensures string inputs are handled correctly.
romanticFactor = Math.max(0, Math.min(1, parseFloat(romanticFactor)));
softness = Math.max(0, Math.min(5, parseFloat(softness))); // Max blur 5px for a "soft" effect.
vignetteIntensity = Math.max(0, Math.min(1, parseFloat(vignetteIntensity)));
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can be an optimization hint for repeated getImageData/putImageData calls.
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;
// 1. Apply Softness (Blur)
// The canvas filter is applied when drawImage is called.
// Only apply blur if softness is meaningfully greater than a very small threshold (e.g., 0.01).
if (softness > 0.01) {
ctx.filter = `blur(${softness}px)`;
}
// Draw the original image onto the canvas. If a filter was set, it will be applied here.
ctx.drawImage(originalImg, 0, 0, w, h);
// Reset the filter on the context to 'none'. This ensures that subsequent drawing operations
// (like the vignette) are not affected by the blur filter.
if (softness > 0.01) {
ctx.filter = 'none';
}
// 2. Apply Romantic Color Grading (Desaturation + Tint)
// This pixel-level processing is done only if romanticFactor indicates an effect should be applied.
if (romanticFactor > 0.01) { // Apply color grading if factor is meaningfully greater than a small threshold.
// Get the pixel data from the canvas. At this point, the canvas contains the (potentially blurred) image.
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data; // This is a Uint8ClampedArray: [R,G,B,A, R,G,B,A, ...]
// Define the target tint color (a soft, warm rose/peach hue).
const tintR_val = 230; // Red component of the tint color
const tintG_val = 200; // Green component of the tint color
const tintB_val = 200; // Blue component of the tint color
// Calculate the effective alpha for tinting, based on romanticFactor.
// This determines how much the original pixel color blends with the tint color. Max 35% blend.
const tintAlpha = 0.35 * romanticFactor;
// Calculate the desaturation level, also based on romanticFactor.
// This imparts a slightly vintage or dreamy feel. Max 25% desaturation.
const desaturationLevel = 0.25 * romanticFactor;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// Apply Desaturation (if desaturationLevel is effective)
// This blends the current color with its grayscale equivalent.
if (desaturationLevel > 0) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Standard luminance calculation for grayscale
r = r * (1 - desaturationLevel) + gray * desaturationLevel;
g = g * (1 - desaturationLevel) + gray * desaturationLevel;
b = b * (1 - desaturationLevel) + gray * desaturationLevel;
}
// Apply Tint overlay
// This blends the (now potentially desaturated) color with the defined tint color.
data[i] = Math.min(255, Math.max(0, r * (1 - tintAlpha) + tintR_val * tintAlpha));
data[i+1] = Math.min(255, Math.max(0, g * (1 - tintAlpha) + tintG_val * tintAlpha));
data[i+2] = Math.min(255, Math.max(0, b * (1 - tintAlpha) + tintB_val * tintAlpha));
// The alpha channel (data[i+3]) is preserved from the original image.
}
// Write the modified pixel data back to the canvas.
ctx.putImageData(imageData, 0, 0);
}
// 3. Apply Vignette
// This adds a darkening effect to the corners and edges of the image.
if (vignetteIntensity > 0.01) { // Apply vignette if intensity is meaningfully greater than a small threshold.
const centerX = w / 2;
const centerY = h / 2;
// Calculate the radius to the furthest corner of the image. This ensures the vignette covers the entire image.
const outerRadius = Math.sqrt(Math.pow(w / 2, 2) + Math.pow(h / 2, 2));
// Determine the inner radius of the vignette (the central, fully transparent part).
// This radius shrinks as vignetteIntensity increases, making the vignette effect more pronounced.
// The 0.75 factor controls how rapidly the vignette "closes in" towards the center.
const innerRadius = outerRadius * (1 - (vignetteIntensity * 0.75));
// Create a radial gradient. The gradient transitions from transparent in the center to dark at the edges.
const gradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
// Define the color stops for the gradient.
gradient.addColorStop(0, 'rgba(0,0,0,0)'); // At the innerRadius, the gradient is fully transparent black.
// Determine the opacity of the vignette at the extreme edges.
// This is controlled by vignetteIntensity, with a maximum of 80% black opacity.
const edgeOpacity = vignetteIntensity * 0.8;
gradient.addColorStop(1, `rgba(0,0,0,${edgeOpacity})`); // At the outerRadius, the gradient is semi-transparent black.
ctx.fillStyle = gradient;
// Draw the gradient rectangle over the entire canvas using the default 'source-over' compositing mode.
// This overlays the semi-transparent gradient on top of the existing image content.
ctx.fillRect(0, 0, w, h);
}
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 Romantic Photo Filter Tool enhances your images by applying a soft, romantic effect that can make your photos appear dreamy and warm. Users can customize the level of softness, color tint, and vignette intensity to create a unique look that suits their style. This tool is ideal for personalizing memories, creating stunning visuals for social media, and adding a touch of romance to portraits or landscape photography.