You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Generates a "Variable Glitch Image" effect.
*
* @param {HTMLImageElement} originalImg - The source image object.
* @param {number|string} intensity - Maximum horizontal pixel shift for glitch slices.
* @param {number|string} rgbShift - Offset for the RGB channel separation.
* @param {number|string} slices - Number of horizontal corrupted slices.
* @param {number|string} scanlines - Opacity for the scanline overlay artifact (0 to 1).
* @returns {HTMLCanvasElement} - A canvas containing the glitch processed image.
*/
function processImage(originalImg, intensity = 40, rgbShift = 10, slices = 20, scanlines = 0.15) {
// Coerce parameters to numbers
intensity = Math.abs(Number(intensity)) || 0;
rgbShift = Number(rgbShift) || 0;
slices = Number(slices) || 0;
scanlines = Number(scanlines) || 0;
const width = originalImg.width;
const height = originalImg.height;
// Setup working canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Draw the clean base image
ctx.drawImage(originalImg, 0, 0);
// Apply Slice Corruptions
for (let i = 0; i < slices; i++) {
const y = Math.floor(Math.random() * height);
// Randomize slice thickness (mix of thin artifacts and thicker block chunks)
let h;
if (Math.random() > 0.8) {
h = Math.max(1, Math.floor(Math.random() * (height * 0.15))); // up to 15% height
} else {
h = Math.max(1, Math.floor(Math.random() * (height * 0.02))); // thin slice
}
// Decide shift direction & magnitude
const dir = Math.random() > 0.5 ? 1 : -1;
const offsetWidth = Math.floor(Math.random() * intensity) * dir;
if (offsetWidth === 0) continue;
// Clear the destination row to preserve accurate alpha handling on transparent images
ctx.clearRect(0, y, width, h);
// Draw the shifted center chunk
ctx.drawImage(originalImg, 0, y, width, h, offsetWidth, y, width, h);
// Wrap around the edges for that continuous data failure look
if (offsetWidth > 0) {
ctx.drawImage(originalImg, width - offsetWidth, y, offsetWidth, h, 0, y, offsetWidth, h);
} else {
const absOffset = Math.abs(offsetWidth);
ctx.drawImage(originalImg, 0, y, absOffset, h, width - absOffset, y, absOffset, h);
}
// Randomly tint a few slices (cyan or red) to mimic chromatic failure
if (Math.random() > 0.75) {
ctx.fillStyle = Math.random() > 0.5 ? 'rgba(255, 0, 0, 0.25)' : 'rgba(0, 255, 255, 0.25)';
ctx.globalCompositeOperation = 'source-atop';
ctx.fillRect(0, y, width, h);
ctx.globalCompositeOperation = 'source-over'; // reset
}
}
// Apply RGB Split (Chromatic Aberration)
const shiftOffset = Math.floor(rgbShift);
if (shiftOffset !== 0) {
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const newImgData = ctx.createImageData(width, height);
const newData = newImgData.data;
const totalPixels = width * height;
// Perform a linear array shift to allow realistic scanline spillover
for (let i = 0; i < totalPixels; i++) {
const px = i * 4;
// Shifted pixel indices
let rPx = (i - shiftOffset) * 4;
let bPx = (i + shiftOffset) * 4;
// Clamp to avoid out-of-bounds index crashing
if (rPx < 0) rPx = px;
if (bPx >= data.length) bPx = px;
newData[px] = data[rPx]; // Red Channel (shifted)
newData[px + 1] = data[px + 1]; // Green Channel (unshifted)
newData[px + 2] = data[bPx + 2]; // Blue Channel (shifted)
newData[px + 3] = data[px + 3]; // Alpha channel (unshifted to maintain edges)
}
ctx.putImageData(newImgData, 0, 0);
}
// Apply Scanline Artifacts
if (scanlines > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${Math.min(1, scanlines)})`;
// Draw 1px horizontal black bars on every 3rd line
for (let y = 0; y < height; y += 3) {
ctx.fillRect(0, y, width, 1);
}
}
return canvas;
}
Apply Changes