You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Applies a poor analog television reception filter to an image.
*
* This function simulates several common artifacts of old analog TV signals, including:
* - Desaturation: Colors are washed out.
* - Noise/Static: Random pixel noise is added across the image.
* - Scanlines: Faint horizontal lines are simulated.
* - Vertical Jitter: The entire image is slightly shifted up or down.
* - Horizontal Sync Glitch: Horizontal bands of the image are sporadically shifted left or right.
* - Vignette: The corners of the image are darkened to mimic a CRT screen.
*
* @param {HTMLImageElement} originalImg The original image object to process.
* @param {number} [noiseIntensity=0.4] The intensity of the static noise (0 to 1).
* @param {number} [scanlineIntensity=0.2] The visibility of the horizontal scanlines (0 to 1).
* @param {number} [desaturation=0.4] The amount to desaturate the image colors (0 to 1).
* @param {number} [horizontalJitter=4] The maximum horizontal shift in pixels for sync glitches.
* @param {number} [verticalJitter=4] The maximum vertical shift in pixels for frame roll.
* @param {number} [vignetteIntensity=0.7] The darkness of the vignette at the corners (0 to 1).
* @returns {HTMLCanvasElement} A new canvas element with the filter applied.
*/
function processImage(originalImg, noiseIntensity = 0.4, scanlineIntensity = 0.2, desaturation = 0.4, horizontalJitter = 4, verticalJitter = 4, vignetteIntensity = 0.7) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
willReadFrequently: true
});
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
canvas.width = width;
canvas.height = height;
// --- 1. Vertical Jitter ---
// Randomly shift the whole image vertically to simulate a vertical hold issue.
const yShift = (Math.random() - 0.5) * verticalJitter;
ctx.drawImage(originalImg, 0, yShift, width, height);
// --- 2. Horizontal Jitter ---
// To simulate horizontal sync issues, copy the image to a temporary canvas
// and then draw it back in shifted horizontal slices.
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = width;
tempCanvas.height = height;
tempCtx.drawImage(canvas, 0, 0, width, height);
// Clear the main canvas to redraw the slices.
ctx.clearRect(0, 0, width, height);
const numSlices = 40; // More slices give a more detailed jitter.
const sliceHeight = Math.max(1, Math.floor(height / numSlices));
for (let i = 0; i < numSlices; i++) {
const y = i * sliceHeight;
// Make jitter happen sporadically for a more "glitchy" feel.
const xShift = (Math.random() > 0.95) ? (Math.random() - 0.5) * horizontalJitter : 0;
// Ensure we don't draw outside the source canvas bounds.
const sY = y;
const sH = Math.min(sliceHeight, tempCanvas.height - sY);
if (sH <= 0) continue;
ctx.drawImage(
tempCanvas,
0, sY, width, sH, // source rect
xShift, y, width, sH // destination rect
);
}
// --- 3. Pixel-level effects (Desaturation, Noise, Scanlines) ---
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const y = Math.floor((i / 4) / width);
// --- Desaturation ---
const r = data[i],
g = data[i + 1],
b = data[i + 2];
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
data[i] = r * (1 - desaturation) + luminance * desaturation;
data[i + 1] = g * (1 - desaturation) + luminance * desaturation;
data[i + 2] = b * (1 - desaturation) + luminance * desaturation;
// --- Noise ---
const noise = (Math.random() - 0.5) * 255 * noiseIntensity;
data[i] += noise;
data[i + 1] += noise;
data[i + 2] += noise;
// --- Scanlines ---
// Darken every second line.
if (y % 2 === 0) {
const darken = 1.0 - scanlineIntensity;
data[i] *= darken;
data[i + 1] *= darken;
data[i + 2] *= darken;
}
// Clamp values to the 0-255 range.
data[i] = Math.max(0, Math.min(255, data[i]));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1]));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2]));
}
ctx.putImageData(imageData, 0, 0);
// --- 4. Vignette ---
// Add a dark vignette to simulate the old CRT screen curvature.
const outerRadius = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2));
const gradient = ctx.createRadialGradient(
width / 2, height / 2, 0,
width / 2, height / 2, outerRadius
);
gradient.addColorStop(0.5, 'rgba(0,0,0,0)'); // Center is transparent
gradient.addColorStop(1, `rgba(0,0,0,${vignetteIntensity})`); // Edge is dark
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
return canvas;
}
Apply Changes