You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies a cinematic, old Bollywood-inspired aesthetic to an input image.
* This function adds warm vintage tones, faded colors, soft film grain, a vignette,
* and a rain drizzle effect to evoke a sense of nostalgia and poetic romance.
*
* @param {Image} originalImg The original javascript Image object to process.
* @param {number} [grainAmount=20] The intensity of the film grain. Recommended range: 0-50.
* @param {number} [warmth=40] The intensity of the warm, vintage color grading. Range: 0-100.
* @param {number} [fade=30] The amount of color fading (desaturation). Range: 0-100.
* @param {number} [rainDensity=500] The number of raindrops to render. Recommended range: 0-1500.
* @returns {HTMLCanvasElement} A new canvas element with the processed image.
*/
function processImage(originalImg, grainAmount = 20, warmth = 40, fade = 30, rainDensity = 500) {
// 1. Canvas Setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
// 2. Initial Draw & Softness
// A subtle blur adds to the dreamy, soft-focus, cinematic feel.
ctx.filter = 'blur(0.5px)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none'; // Reset filter to not affect subsequent drawings
// 3. Pixel-level manipulation: Color Grading, Fading, Grain
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
// Convert percentage-based parameters to factors between 0 and 1
const grainFactor = Math.max(0, grainAmount);
const warmthFactor = Math.max(0, Math.min(100, warmth)) / 100;
const fadeFactor = Math.max(0, Math.min(100, fade)) / 100;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// --- a. Warm Vintage Tones (Sepia-like tint) ---
// Apply a classic sepia transformation and blend it based on the warmthFactor
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 - warmthFactor) + tr * warmthFactor;
g = g * (1 - warmthFactor) + tg * warmthFactor;
b = b * (1 - warmthFactor) + tb * warmthFactor;
// --- b. Faded Colors (Desaturation) ---
// Calculate the grayscale value and blend it with the color based on fadeFactor
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
r = r * (1 - fadeFactor) + gray * fadeFactor;
g = g * (1 - fadeFactor) + gray * fadeFactor;
b = b * (1 - fadeFactor) + gray * fadeFactor;
// --- c. Soft Film Grain ---
// Add monochromatic noise for a realistic grain effect
const noise = (Math.random() - 0.5) * grainFactor;
r += noise;
g += noise;
b += noise;
// Clamp values to the valid 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);
// 4. Cinematic Vignette
// This darkens the edges to draw focus to the center, simulating a cinematic look.
const centerX = w / 2;
const centerY = h / 2;
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
const gradient = ctx.createRadialGradient(centerX, centerY, outerRadius * 0.4, centerX, centerY, outerRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.6)');
ctx.globalCompositeOperation = 'multiply';
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation
// 5. Rain Drizzle Effect
ctx.strokeStyle = 'rgba(200, 200, 220, 0.5)';
ctx.lineWidth = 1;
ctx.lineCap = 'round';
for (let i = 0; i < rainDensity; i++) {
const x1 = Math.random() * w;
const y1 = Math.random() * h;
const length = Math.random() * 20 + 10; // Raindrop length varies
const angle = 0.1; // A slight, consistent slant
const x2 = x1 - length * angle;
const y2 = y1 + length;
// Varying opacity creates a sense of depth for the raindrops
ctx.globalAlpha = Math.random() * 0.4 + 0.2;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
ctx.globalAlpha = 1.0; // Reset global alpha
// 6. Return the final canvas
return canvas;
}
Apply Changes