You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies a visual filter to an image to simulate the look and feel of a specific movie genre.
* @param {Image} originalImg The original javascript Image object.
* @param {string} genre The movie genre style to apply. Supported genres: "Film Noir", "Western", "Sci-Fi", "Horror", "Romance", "Vintage".
* @returns {HTMLCanvasElement} A new canvas element with the genre filter applied.
*/
function processImage(originalImg, genre = 'Film Noir') {
// 1. Create a canvas and get its 2D context.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 2. Set canvas dimensions to match the original image's natural dimensions.
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// 3. Use a switch statement to apply effects based on the selected genre.
// The genre string is normalized to handle variations in capitalization.
switch (genre.trim().toLowerCase()) {
case 'film noir':
// High contrast, black and white, with film grain.
ctx.filter = 'grayscale(1) contrast(1.6) brightness(0.8)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none'; // Remove filter to add grain on top of the filtered image.
const imageDataNoir = ctx.getImageData(0, 0, w, h);
const dataNoir = imageDataNoir.data;
const noiseAmount = 25;
for (let i = 0; i < dataNoir.length; i += 4) {
const noise = (Math.random() - 0.5) * noiseAmount;
// Add noise to each channel and clamp the value between 0 and 255.
dataNoir[i] = Math.max(0, Math.min(255, dataNoir[i] + noise));
dataNoir[i + 1] = Math.max(0, Math.min(255, dataNoir[i + 1] + noise));
dataNoir[i + 2] = Math.max(0, Math.min(255, dataNoir[i + 2] + noise));
}
ctx.putImageData(imageDataNoir, 0, 0);
break;
case 'western':
// Sepia tone, slightly increased contrast, and a dusty overlay.
ctx.filter = 'sepia(0.7) contrast(1.2) brightness(0.95)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Add a dusty, warm overlay.
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = 'rgba(189, 147, 83, 0.15)'; // Burnt sienna
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation.
break;
case 'sci-fi':
// Teal and orange color grading with increased contrast.
ctx.filter = 'contrast(1.2) saturate(1.1)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Add the classic teal/orange gradient overlay.
ctx.globalCompositeOperation = 'soft-light';
const sciFiGradient = ctx.createLinearGradient(0, h, w, 0); // Diagonal
sciFiGradient.addColorStop(0.1, 'rgba(0, 255, 255, 0.3)'); // Teal/Cyan
sciFiGradient.addColorStop(0.9, 'rgba(255, 140, 0, 0.3)'); // Orange
ctx.fillStyle = sciFiGradient;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over';
break;
case 'horror':
// Dark, desaturated, high contrast, with a cool tint and vignetting.
ctx.filter = 'contrast(1.4) brightness(0.7) saturate(0.3)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Add a cool, dark blue tint.
ctx.globalCompositeOperation = 'multiply';
ctx.fillStyle = 'rgba(0, 20, 80, 0.2)';
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over';
// Add a dark vignette.
const outerRadius = Math.sqrt(Math.pow(w / 2, 2) + Math.pow(h / 2, 2));
const innerRadius = outerRadius * 0.4;
const horrorGradient = ctx.createRadialGradient(w / 2, h / 2, innerRadius, w / 2, h / 2, outerRadius);
horrorGradient.addColorStop(0, 'rgba(0,0,0,0)');
horrorGradient.addColorStop(1, 'rgba(0,0,0,0.8)');
ctx.fillStyle = horrorGradient;
ctx.fillRect(0, 0, w, h);
break;
case 'romance':
// Warm, saturated, soft-focus look with a gentle glow.
ctx.filter = 'saturate(1.2) contrast(1.1) brightness(1.05) blur(0.7px)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Add a soft, warm glow.
ctx.globalCompositeOperation = 'soft-light';
ctx.fillStyle = 'rgba(255, 180, 180, 0.25)'; // Soft pink
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'source-over';
break;
case 'vintage':
// Sepia tone with procedurally generated scratches and dust specks.
ctx.filter = 'sepia(0.6) contrast(1.1) brightness(0.9)';
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none';
// Add vertical scratches.
const numScratches = Math.floor(w / 35);
for (let i = 0; i < numScratches; i++) {
const x = Math.random() * w;
ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.3 + 0.1})`;
ctx.lineWidth = Math.random() * 1.2 + 0.5;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x + (Math.random() - 0.5) * 10, h);
ctx.stroke();
}
// Add dust and specks.
const numSpecks = w * h / 400;
for (let i = 0; i < numSpecks; i++) {
const x = Math.random() * w;
const y = Math.random() * h;
const size = Math.random() * 1.5;
const opacity = Math.random() * 0.6;
ctx.fillStyle = (Math.random() > 0.5) ? `rgba(0, 0, 0, ${opacity})` : `rgba(255, 255, 255, ${opacity})`;
ctx.fillRect(x, y, size, size);
}
break;
default:
// If the genre is not recognized, just draw the original image.
ctx.drawImage(originalImg, 0, 0, w, h);
break;
}
// 4. Return the modified canvas element.
return canvas;
}
Apply Changes