You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, fillMode = "blur-bg", colorGrading = "teal-orange", vignetteStrength = 0.6, subtitleText = "") {
// 9:16 Aspect Ratio Dimensions (Standard 1080x1920 for vertical movie formats like TikTok/Reels)
const targetW = 1080;
const targetH = 1920;
const canvas = document.createElement("canvas");
canvas.width = targetW;
canvas.height = targetH;
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const ow = originalImg.width;
const oh = originalImg.height;
// 1. Draw Background
if (fillMode === "blur-bg") {
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, targetW, targetH);
// Scale to cover
let scaleCover = Math.max(targetW / ow, targetH / oh);
let wCover = ow * scaleCover;
let hCover = oh * scaleCover;
ctx.filter = "blur(45px) brightness(0.4)";
ctx.drawImage(originalImg, (targetW - wCover) / 2, (targetH - hCover) / 2, wCover, hCover);
ctx.filter = "none";
} else {
// "black-bars" or other options
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, targetW, targetH);
}
// 2. Draw Main Image Focus
if (fillMode === "crop") {
let scaleCover = Math.max(targetW / ow, targetH / oh);
let wCover = ow * scaleCover;
let hCover = oh * scaleCover;
ctx.drawImage(originalImg, (targetW - wCover) / 2, (targetH - hCover) / 2, wCover, hCover);
} else {
// Draw centered maintaining original aspect ratio
let scaleContain = Math.min(targetW / ow, targetH / oh);
let wContain = ow * scaleContain;
let hContain = oh * scaleContain;
ctx.drawImage(originalImg, (targetW - wContain) / 2, (targetH - hContain) / 2, wContain, hContain);
}
// 3. Apply Cinematic Color Grading (Pixel Manipulation)
if (colorGrading !== "none") {
const imgData = ctx.getImageData(0, 0, targetW, targetH);
const data = imgData.data;
// Contrast adjustment multiplier
const contrastLevel = 35;
const factor = (259 * (contrastLevel + 255)) / (255 * (259 - contrastLevel));
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// Increase Contrast to simulate dramatic movie look
r = factor * (r - 128) + 128;
g = factor * (g - 128) + 128;
b = factor * (b - 128) + 128;
if (colorGrading === "teal-orange") {
// Calculate Luminance
let luma = 0.299 * r + 0.587 * g + 0.114 * b;
// -1 (shadows) to +1 (highlights)
let mix = (luma - 128) / 128;
if (mix > 0) {
// Highlights -> Warmer (Orange tint)
r += mix * 35;
g += mix * 10;
b -= mix * 30;
} else {
// Shadows -> Cooler (Teal tint)
// mix is negative, so adding mix subtracts the value
r += mix * 40; // Drops red
b -= mix * 45; // Increases blue (double negative)
g -= mix * 20; // Increases green slightly, yielding teal
}
} else if (colorGrading === "vintage") {
// Sepia conversion
let tr = 0.393 * r + 0.769 * g + 0.189 * b;
let tg = 0.349 * r + 0.686 * g + 0.168 * b;
let tb = 0.272 * r + 0.534 * g + 0.131 * b;
r = tr; g = tg; b = tb;
}
// Add subtle cinematic film grain
let grain = (Math.random() - 0.5) * 12;
r += grain;
g += grain;
b += grain;
// Clamp values between 0 and 255
data[i] = Math.min(255, Math.max(0, r));
data[i+1] = Math.min(255, Math.max(0, g));
data[i+2] = Math.min(255, Math.max(0, b));
}
ctx.putImageData(imgData, 0, 0);
}
// 4. Apply Vignette (Darkened Edges)
if (vignetteStrength > 0) {
let maxDim = Math.max(targetW, targetH);
let gradient = ctx.createRadialGradient(
targetW / 2, targetH / 2, maxDim * 0.3,
targetW / 2, targetH / 2, maxDim * 0.7
);
gradient.addColorStop(0, "rgba(0,0,0,0)");
gradient.addColorStop(1, `rgba(0,0,0,${Math.min(vignetteStrength, 1)})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, targetW, targetH);
}
// 5. Draw Optional Cinematic Subtitle
if (subtitleText && subtitleText.trim() !== "") {
const fontSize = 48;
ctx.font = `italic ${fontSize}px Arial, sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Movie Subtitle Shadow/Stroke Effect
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillStyle = "#f1c40f"; // Standard cinematic yellow for readability
// Basic multi-line word wrapping logic
const words = subtitleText.split(" ");
let line = "";
let lines = [];
for(let n = 0; n < words.length; n++) {
let testLine = line + words[n] + " ";
if(ctx.measureText(testLine).width > targetW * 0.85 && n > 0) {
lines.push(line);
line = words[n] + " ";
} else {
line = testLine;
}
}
lines.push(line);
// Position text near the bottom quarter
let startY = targetH * 0.85;
for(let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], targetW / 2, startY + (i * fontSize * 1.3));
}
// Reset shadow
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
return canvas;
}
Apply Changes