You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, desaturation = 0.9, contrast = 70, noise = 20, tintColor = "blue", tintOpacity = 0.15) {
// 1. Setup Canvas
const canvas = document.createElement('canvas');
// Use willReadFrequently: true as getImageData will be called.
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 2. Get Image Data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Helper function: Parse color string to RGB object
function parseColorToRGB(colorStr) {
const s = String(colorStr || "").trim().toLowerCase();
if (s === "" || s === "none") return null;
const tempCanvas = document.createElement('canvas');
tempCanvas.width = tempCanvas.height = 1;
// Use willReadFrequently: true for temporary canvas as well
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.fillStyle = s; // Use the processed string (e.g. "blue", "#FF0000")
tempCtx.fillRect(0, 0, 1, 1);
const pixelData = tempCtx.getImageData(0, 0, 1, 1).data;
// If fillStyle was invalid, it usually defaults to black (0,0,0,255).
// This is acceptable as a resulting tint color.
return { r: pixelData[0], g: pixelData[1], b: pixelData[2] };
}
const tintRGB = parseColorToRGB(tintColor);
// Helper function: Clamp value between min and max
function clamp(value, min = 0, max = 255) {
return Math.max(min, Math.min(value, max));
}
// Pre-calculate contrast factor
// Map contrast parameter [0, 100] to a new range, e.g., [-127, 127] for the formula.
// A contrast value of 50 means no change (maps to 0).
const contrastInternalValue = (Number(contrast) - 50) * 2.54; // Maps [0, 100] to approx [-127, 127]
const contrastFactor = (259 * (contrastInternalValue + 255)) / (255 * (259 - contrastInternalValue));
// 3. Pixel Manipulation Loop
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// a. Desaturation
// desaturation: 0 = original color, 1 = full grayscale
if (desaturation > 0) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
const desat = Number(desaturation);
r = r * (1 - desat) + gray * desat;
g = g * (1 - desat) + gray * desat;
b = b * (1 - desat) + gray * desat;
}
// b. Contrast Adjustment
// contrast: 50 means no change
if (Number(contrast) !== 50) {
r = clamp(contrastFactor * (r - 128) + 128);
g = clamp(contrastFactor * (g - 128) + 128);
b = clamp(contrastFactor * (b - 128) + 128);
}
// c. Noise
// noise: intensity of noise (0 = no noise)
if (noise > 0) {
const noiseAmount = Number(noise);
// Generate random noise value between -noiseAmount and +noiseAmount
const noiseVal = (Math.random() - 0.5) * 2 * noiseAmount;
r = clamp(r + noiseVal);
g = clamp(g + noiseVal);
b = clamp(b + noiseVal);
}
// d. Color Tint
// tintOpacity: 0 = no tint, 1 = full tint color
if (tintRGB && tintOpacity > 0) {
const tOp = Number(tintOpacity);
r = clamp(r * (1 - tOp) + tintRGB.r * tOp);
g = clamp(g * (1 - tOp) + tintRGB.g * tOp);
b = clamp(b * (1 - tOp) + tintRGB.b * tOp);
}
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
// Alpha channel (data[i+3]) remains unchanged
}
// 4. Put Image Data Back onto the canvas
ctx.putImageData(imageData, 0, 0);
// 5. Return Canvas
return canvas;
}
Apply Changes