You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, scanLineWidth = 1, scanLineGap = 4, scanLineAlpha = 0.2, curveAmount = 0.1, vignetteAlpha = 0.5, noiseAmount = 0.1) {
// --- Helper function to ensure parameters are numbers ---
const pScanLineWidth = Number(scanLineWidth);
const pScanLineGap = Number(scanLineGap);
const pScanLineAlpha = Number(scanLineAlpha);
const pCurveAmount = Number(curveAmount);
const pVignetteAlpha = Number(vignetteAlpha);
const pNoiseAmount = Number(noiseAmount);
// --- 1. Setup Canvas ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
alpha: true
});
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// --- 2. Apply Curvature Effect ---
// This effect simulates the curved glass of a CRT screen.
// It works by drawing the image in thin vertical and horizontal slices,
// offsetting and scaling them to create a barrel distortion illusion.
if (pCurveAmount > 0) {
// Use a temporary canvas to hold the intermediate drawing states
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = width;
tempCanvas.height = height;
tempCtx.drawImage(originalImg, 0, 0, width, height);
const centerX = width / 2;
const centerY = height / 2;
// Pass 1: Apply vertical distortion
for (let x = 0; x < width; x++) {
const dx = x - centerX;
const normX = dx / centerX; // Normalize x-coordinate to [-1, 1]
// Use a parabolic formula to calculate the vertical offset and scaling
const yOffset = (normX * normX) * (height * pCurveAmount * 0.5);
const sliceHeight = height - 2 * yOffset;
if (sliceHeight > 0) {
ctx.drawImage(
tempCanvas, // source canvas
x, 0, 1, height, // source rect (a 1px wide slice)
x, yOffset, 1, sliceHeight // destination rect (scaled and offset)
);
}
}
// Copy the vertically distorted image back to the temp canvas for the next pass
tempCtx.clearRect(0, 0, width, height);
tempCtx.drawImage(canvas, 0, 0, width, height);
ctx.clearRect(0, 0, width, height);
// Pass 2: Apply horizontal distortion
for (let y = 0; y < height; y++) {
const dy = y - centerY;
const normY = dy / centerY; // Normalize y-coordinate to [-1, 1]
const xOffset = (normY * normY) * (width * pCurveAmount * 0.5);
const sliceWidth = width - 2 * xOffset;
if (sliceWidth > 0) {
ctx.drawImage(
tempCanvas, // source canvas
0, y, width, 1, // source rect (a 1px high slice)
xOffset, y, sliceWidth, 1 // destination rect (scaled and offset)
);
}
}
} else {
// If no curve, just draw the original image
ctx.drawImage(originalImg, 0, 0, width, height);
}
// --- 3. Apply Noise Effect ---
// Adds random noise to simulate analog signal interference.
if (pNoiseAmount > 0) {
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const noise = (Math.random() - 0.5) * 255 * pNoiseAmount;
// Add noise to R, G, and B channels, clamping values between 0 and 255
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);
}
// --- 4. Apply Scan Lines Effect ---
// Overlays dark horizontal lines to mimic the raster scan of a CRT.
if (pScanLineAlpha > 0 && pScanLineWidth > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${pScanLineAlpha})`;
const totalLineHeight = pScanLineWidth + pScanLineGap;
if (totalLineHeight > 0) {
for (let y = 0; y < height; y += totalLineHeight) {
ctx.fillRect(0, y, width, pScanLineWidth);
}
}
}
// --- 5. Apply Vignette Effect ---
// Darkens the corners to simulate the falloff of light on a CRT screen.
if (pVignetteAlpha > 0) {
const centerX = width / 2;
const centerY = height / 2;
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
const innerRadius = outerRadius * (1 - pVignetteAlpha);
const gradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
// Make the vignette a bit stronger at the edges and cap its alpha at 1
const finalVignetteAlpha = Math.min(1, pVignetteAlpha * 1.5);
gradient.addColorStop(1, `rgba(0,0,0,${finalVignetteAlpha})`);
ctx.fillStyle = gradient;
ctx.globalCompositeOperation = 'source-over';
ctx.fillRect(0, 0, width, height);
}
// --- 6. Return the final canvas ---
return canvas;
}
Apply Changes