You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, intensity = 0.7, colorTwistFactor = 50, vignetteStrength = 0.4, contrastValue = 1.1) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Added willReadFrequently for potential performance optimization
// Ensure the original image is loaded before trying to get its dimensions
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
if (width === 0 || height === 0) {
// Return an empty canvas or handle error if image has no dimensions
canvas.width = 1;
canvas.height = 1;
console.warn("Image has zero width or height.");
return canvas;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(originalImg, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
const centerX = width / 2;
const centerY = height / 2;
// Calculate maxDist for vignette from center to a corner for accurate normalization
const maxDist = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
// Clamp utility
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// 1. Contrast Adjustment
// This formula scales pixel values around the midpoint (127.5)
// contrastValue = 1.0 means no change. > 1 increases contrast, < 1 decreases.
if (contrastValue !== 1.0) {
r = ((r / 255.0 - 0.5) * contrastValue + 0.5) * 255.0;
g = ((g / 255.0 - 0.5) * contrastValue + 0.5) * 255.0;
b = ((b / 255.0 - 0.5) * contrastValue + 0.5) * 255.0;
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
}
const contrastedR = r;
const contrastedG = g;
const contrastedB = b;
// 2. Surreal Color Transformation (applied to contrasted colors)
// Uses sine waves based on other channel values to create "twisted" colors
let tr = contrastedR + colorTwistFactor * Math.sin((contrastedG / 255.0) * Math.PI * 2.0);
let tg = contrastedG + colorTwistFactor * Math.sin((contrastedB / 255.0) * Math.PI * 2.0);
let tb = contrastedB + colorTwistFactor * Math.sin((contrastedR / 255.0) * Math.PI * 2.0);
tr = clamp(tr, 0, 255);
tg = clamp(tg, 0, 255);
tb = clamp(tb, 0, 255);
// 3. Apply Intensity
// Blends between the contrasted color and the "surreal-transformed" contrasted color
let finalR = contrastedR * (1.0 - intensity) + tr * intensity;
let finalG = contrastedG * (1.0 - intensity) + tg * intensity;
let finalB = contrastedB * (1.0 - intensity) + tb * intensity;
finalR = clamp(finalR, 0, 255);
finalG = clamp(finalG, 0, 255);
finalB = clamp(finalB, 0, 255);
// 4. Vignette
if (vignetteStrength > 0 && maxDist > 0) {
const pixelX = (i / 4) % width;
const pixelY = Math.floor((i / 4) / width);
const dx = pixelX - centerX;
const dy = pixelY - centerY;
const dist = Math.sqrt(dx * dx + dy * dy);
const normDist = dist / maxDist; // Normalized distance: 0 at center, 1 at corners
// Vignette factor `k`: 1 at center, decreases towards edges
// Using power 1.5 for a smooth, noticeable vignette. Power 2 is also common.
const k = 1.0 - vignetteStrength * Math.pow(normDist, 1.5);
finalR *= clamp(k, 0, 1); // k can be < 0 if vignetteStrength * normDist^1.5 > 1
finalG *= clamp(k, 0, 1);
finalB *= clamp(k, 0, 1);
finalR = clamp(finalR, 0, 255);
finalG = clamp(finalG, 0, 255);
finalB = clamp(finalB, 0, 255);
}
data[i] = finalR;
data[i + 1] = finalG;
data[i + 2] = finalB;
// Alpha (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes