You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies a cinematic, "video frame" style effect to an image by adding letterboxing,
* a color grade, film grain, and a vignette.
*
* @param {Image} originalImg The original javascript Image object.
* @param {string} [aspectRatio='16:9'] The target aspect ratio for the letterboxing, e.g., '16:9', '4:3', '2.35:1'.
* @param {string} [colorGrade='rgba(255, 180, 100, 0.15)'] The CSS color string for the color grading overlay.
* @param {number} [grainAmount=0.08] The intensity of the film grain effect. Recommended range: 0 to 0.2.
* @param {number} [vignetteStrength=0.6] The strength and size of the vignette effect. Range: 0 (off) to 1 (strong).
* @returns {HTMLCanvasElement} A new canvas element with the video effects applied.
*/
function processImage(originalImg, aspectRatio = '16:9', colorGrade = 'rgba(255, 180, 100, 0.15)', grainAmount = 0.08, vignetteStrength = 0.6) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
alpha: false
});
// 1. Calculate dimensions for letterboxing
const [aspectW, aspectH] = aspectRatio.split(':').map(Number);
if (!aspectW || !aspectH || isNaN(aspectW) || isNaN(aspectH)) {
console.error("Invalid aspect ratio format. Using 16:9.");
aspectRatio = 16 / 9;
} else {
aspectRatio = aspectW / aspectH;
}
const originalAspectRatio = originalImg.naturalWidth / originalImg.naturalHeight;
let newWidth, newHeight, drawX, drawY;
// Determine the final canvas size based on the target aspect ratio
if (originalAspectRatio > aspectRatio) {
// Original image is wider than the target aspect ratio (add bars top/bottom)
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalWidth / aspectRatio;
} else {
// Original image is taller or same aspect ratio (add bars left/right)
canvas.height = originalImg.naturalHeight;
canvas.width = originalImg.naturalHeight * aspectRatio;
}
// Center the original image within the new canvas dimensions
drawX = (canvas.width - originalImg.naturalWidth) / 2;
drawY = (canvas.height - originalImg.naturalHeight) / 2;
// 2. Draw background and image
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height); // Black background for letterbox
ctx.drawImage(originalImg, drawX, drawY, originalImg.naturalWidth, originalImg.naturalHeight);
// 3. Apply color grading
if (colorGrade && colorGrade !== 'none') {
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = colorGrade;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'source-over'; // Reset composite operation
}
// 4. Apply film grain
if (grainAmount > 0) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Iterate over each pixel
for (let i = 0; i < data.length; i += 4) {
// Don't add grain to pure black letterbox bars
if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0) continue;
const noise = (Math.random() - 0.5) * 255 * grainAmount;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise));
}
ctx.putImageData(imageData, 0, 0);
}
// 5. Apply vignette
if (vignetteStrength > 0) {
const outerRadius = Math.sqrt(Math.pow(canvas.width / 2, 2) + Math.pow(canvas.height / 2, 2));
const gradient = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, outerRadius * (1 - vignetteStrength),
canvas.width / 2, canvas.height / 2, outerRadius
);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.8)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
return canvas;
}
Apply Changes