You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, glitchAmount = 6, ondulationAmplitude = 2, ondulationFrequency = 0.03, scanlineOpacity = 0.15, noiseAmount = 25, vignetteStrength = 0.4) {
// 1. Setup Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
// 2. Step 1: Ondulation (Waviness)
// Create an offscreen canvas to draw the wavy version of the image
const ondulatedCanvas = document.createElement('canvas');
const ondulatedCtx = ondulatedCanvas.getContext('2d');
ondulatedCanvas.width = width;
ondulatedCanvas.height = height;
const phase = Math.random() * Math.PI * 2; // Random phase for a unique wave pattern
for (let y = 0; y < height; y++) {
// Calculate the horizontal offset for this line using a sine wave
const xOffset = Math.sin(y * ondulationFrequency + phase) * ondulationAmplitude;
// Draw a 1px high slice of the original image with the calculated offset
ondulatedCtx.drawImage(originalImg, 0, y, width, 1, xOffset, y, width, 1);
}
// 3. Step 2: Chromatic Aberration & Synthwave Tinting
// Use another offscreen canvas for tinting operations
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = width;
tempCanvas.height = height;
// Set main canvas to blend layers additively (like light)
ctx.globalCompositeOperation = 'lighter';
// Draw Magenta Layer
tempCtx.drawImage(ondulatedCanvas, 0, 0);
tempCtx.globalCompositeOperation = 'multiply';
tempCtx.fillStyle = 'magenta';
tempCtx.fillRect(0, 0, width, height);
// Draw the tinted layer onto the main canvas, shifted left
ctx.drawImage(tempCanvas, -glitchAmount, 0);
// Reset temp canvas for the next color
tempCtx.globalCompositeOperation = 'source-over';
tempCtx.clearRect(0, 0, width, height);
// Draw Cyan Layer
tempCtx.drawImage(ondulatedCanvas, 0, 0);
tempCtx.globalCompositeOperation = 'multiply';
tempCtx.fillStyle = 'cyan';
tempCtx.fillRect(0, 0, width, height);
// Draw the tinted layer onto the main canvas, shifted right
ctx.drawImage(tempCanvas, glitchAmount, 0);
// Reset main canvas compose operation to default
ctx.globalCompositeOperation = 'source-over';
// 4. Step 3: Noise / Grain
if (noiseAmount > 0) {
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// Add random noise to each pixel's RGB values
for (let i = 0; i < data.length; i += 4) {
const noise = (Math.random() - 0.5) * noiseAmount;
data[i] = data[i] + noise;
data[i + 1] = data[i + 1] + noise;
data[i + 2] = data[i + 2] + noise;
}
// The Uint8ClampedArray automatically clamps values between 0 and 255
ctx.putImageData(imageData, 0, 0);
}
// 5. Step 4: Scanlines
if (scanlineOpacity > 0) {
// Use a dark, slightly blue color for a more authentic feel
ctx.fillStyle = `rgba(10, 10, 30, ${scanlineOpacity})`;
for (let y = 0; y < height; y += 3) {
ctx.fillRect(0, y, width, 1);
}
}
// 6. Step 5: CRT Vignette
if (vignetteStrength > 0) {
const centerX = width / 2;
const centerY = height / 2;
// Calculate radius to the corner of the canvas
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
const gradient = ctx.createRadialGradient(centerX, centerY, outerRadius * 0.3, centerX, centerY, outerRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, `rgba(0,0,0,${vignetteStrength})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
}
// 7. Return the final canvas element
return canvas;
}
Apply Changes