You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, sepiaAmountStr = "0.7", noiseAmountStr = "0.1", scanlineFrequencyStr = "4", scanlineStrengthStr = "0.2") {
// Parse and validate parameters, falling back to defaults if parsing fails
let sepiaAmount = parseFloat(sepiaAmountStr);
if (isNaN(sepiaAmount)) sepiaAmount = 0.7;
sepiaAmount = Math.max(0, Math.min(1, sepiaAmount)); // Clamp to [0, 1]
let noiseAmount = parseFloat(noiseAmountStr);
if (isNaN(noiseAmount)) noiseAmount = 0.1;
noiseAmount = Math.max(0, Math.min(1, noiseAmount)); // Clamp to [0, 1]
let scanlineFrequency = parseInt(scanlineFrequencyStr, 10);
if (isNaN(scanlineFrequency) || scanlineFrequency <= 0) scanlineFrequency = 4; // Must be at least 1
scanlineFrequency = Math.max(1, scanlineFrequency);
let scanlineStrength = parseFloat(scanlineStrengthStr);
if (isNaN(scanlineStrength)) scanlineStrength = 0.2;
scanlineStrength = Math.max(0, Math.min(1, scanlineStrength)); // Clamp to [0, 1]
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can be a hint for performance optimization
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Ensure the image has valid dimensions
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
console.warn("Image has no dimensions or is not loaded. Returning a 1x1 canvas.");
canvas.width = 1;
canvas.height = 1;
// Optionally, fill the 1x1 canvas with a color to indicate an issue
// ctx.fillStyle = 'rgba(255,0,0,0.5)';
// ctx.fillRect(0,0,1,1);
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// Get image data to manipulate pixels
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data; // Uint8ClampedArray: [R, G, B, A, R, G, B, A, ...]
// Process each pixel
for (let i = 0; i < pixels.length; i += 4) {
let r = pixels[i];
let g = pixels[i + 1];
let b = pixels[i + 2];
// 1. Apply Sepia tone
if (sepiaAmount > 0) {
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
r = (1 - sepiaAmount) * r + sepiaAmount * tr;
g = (1 - sepiaAmount) * g + sepiaAmount * tg;
b = (1 - sepiaAmount) * b + sepiaAmount * tb;
}
// 2. Apply Noise
if (noiseAmount > 0) {
// Generate noise value between -255*noiseAmount*0.5 and +255*noiseAmount*0.5
const noiseVal = (Math.random() - 0.5) * 255 * noiseAmount;
r += noiseVal;
g += noiseVal;
b += noiseVal;
}
// Clamp RGB values to [0, 255] and round them
pixels[i] = Math.max(0, Math.min(255, Math.round(r)));
pixels[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
pixels[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
// Alpha channel (pixels[i+3]) remains unchanged
}
// 3. Apply Scanlines (after color and noise processing)
if (scanlineFrequency >= 1 && scanlineStrength > 0) {
for (let y = 0; y < canvas.height; y++) {
// Apply scanline if current row (y) is a multiple of scanlineFrequency
if (y % scanlineFrequency === 0) {
for (let x = 0; x < canvas.width; x++) {
const index = (y * canvas.width + x) * 4;
// Reduce brightness of RGB channels for the scanline
pixels[index] = Math.max(0, Math.round(pixels[index] * (1 - scanlineStrength)));
pixels[index + 1] = Math.max(0, Math.round(pixels[index + 1] * (1 - scanlineStrength)));
pixels[index + 2] = Math.max(0, Math.round(pixels[index + 2] * (1 - scanlineStrength)));
}
}
}
}
// Put the modified image data back onto the canvas
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes