You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg,
aberrationAmount = 1.5, // In pixels, how much to shift R and B channels
noiseIntensity = 0.08, // 0 to 1: 0 for no noise, 1 for very high noise
scanlineOpacity = 0.1, // 0 to 1: Opacity of the scanlines (0.1 = 10% darker)
scanlineDensity = 3, // Every Nth line will be a scanline. Use integer >= 1.
jitterIntensity = 0.3, // Multiplier for horizontal jitter/warping effects
desaturation = 0.15) { // 0 to 1: 0 for original colors, 1 for grayscale
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// Ensure scanlineDensity is a positive integer.
const actualScanlineDensity = Math.max(1, Math.floor(scanlineDensity));
const imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
const data = imageData.data;
// Create a pristine copy of the original pixel data for sampling
const originalData = new Uint8ClampedArray(imageData.data);
// Helper function to get a pixel from the originalData, ensures coordinates are within bounds
function getOriginalPixel(x, y) {
x = Math.round(x); // Ensure integer coordinates for pixel indexing
y = Math.round(y);
// Clamp coordinates to be within the image dimensions
x = Math.max(0, Math.min(imgWidth - 1, x));
y = Math.max(0, Math.min(imgHeight - 1, y));
const i = (y * imgWidth + x) * 4;
return [originalData[i], originalData[i + 1], originalData[i + 2], originalData[i + 3]];
}
for (let y = 0; y < imgHeight; y++) {
let lineJitter = 0;
if (jitterIntensity > 0) {
// Base random jitter for the line
lineJitter = (Math.random() - 0.5) * jitterIntensity * 5; // Max displacement: jitterIntensity * 2.5px
// Add a slow sine wave distortion for a "rolling" or "wavy" effect
// Math.random() in phase makes the wave irregular per line
lineJitter += Math.sin(y * 0.075 + Math.random() * Math.PI) * jitterIntensity * 3; // Max displacement: jitterIntensity * 3px
// Occasionally, a more pronounced "glitch" or "sync issue" for a line
// Probability increases slightly with jitterIntensity
if (Math.random() < (0.015 + 0.025 * jitterIntensity)) {
lineJitter += (Math.random() - 0.5) * jitterIntensity * 20; // Max displacement: jitterIntensity * 10px
}
}
for (let x = 0; x < imgWidth; x++) {
const currentIndex = (y * imgWidth + x) * 4;
// 1. Chromatic Aberration & Jitter
// Determine source coordinates for R, G, B channels including line jitter
const jitteredX = x + lineJitter;
const sourceX_r = jitteredX - aberrationAmount;
const sourceX_g = jitteredX;
const sourceX_b = jitteredX + aberrationAmount;
// Sample R, G, B from their respective calculated source coordinates
// Alpha channel is typically taken from the green (central) sample or original non-jittered pixel
let r = getOriginalPixel(sourceX_r, y)[0];
let g = getOriginalPixel(sourceX_g, y)[1];
let b = getOriginalPixel(sourceX_b, y)[2];
let a = getOriginalPixel(sourceX_g, y)[3]; // Alpha from the 'central' G channel sample
// 2. Desaturation
if (desaturation > 0) {
const luminance = 0.299 * r + 0.587 * g + 0.114 * b; // Calculate luminance of the (possibly aberrated) color
const d = Math.max(0, Math.min(1, desaturation)); // Clamp desaturation amount
r = r * (1 - d) + luminance * d;
g = g * (1 - d) + luminance * d;
b = b * (1 - d) + luminance * d;
}
// 3. Noise
if (noiseIntensity > 0) {
const noiseVal = (Math.random() - 0.5) * 255 * noiseIntensity;
r += noiseVal;
g += noiseVal;
b += noiseVal;
}
// 4. Scan Lines
// Darken pixels on specific lines to create scanline effect
if (scanlineOpacity > 0 && (y % actualScanlineDensity === 0)) {
const factor = 1 - Math.max(0, Math.min(1, scanlineOpacity)); // Clamped opacity
r *= factor;
g *= factor;
b *= factor;
}
// Write final pixel data, clamping RGB values to 0-255 range
data[currentIndex] = Math.max(0, Math.min(255, r));
data[currentIndex+1] = Math.max(0, Math.min(255, g));
data[currentIndex+2] = Math.max(0, Math.min(255, b));
data[currentIndex+3] = a; // Use the sampled alpha
}
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image VHS Effect Filter tool allows users to apply a retro VHS-style effect to their images. This effect simulates classic video aesthetics by introducing chromatic aberration, noise, scanlines, and various distortions. It is ideal for creating vintage-style images, enhancing photos with a nostalgic touch, or adding a unique visual style to digital art. Users can customize the intensity of these effects, enabling them to achieve their desired look for personal projects, social media, or creative presentations.