Please bookmark this page to avoid losing your image tool!

Image Computer Glitch Filter Effect Tool

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, intensity = 50) {
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } can be a performance hint for the browser
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); 
    
    // Use naturalWidth/Height for correctly loaded images, fallback to width/height
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;
    
    canvas.width = width;
    canvas.height = height;

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, width, height);

    // If intensity is 0 or less, or image has no dimensions, return the canvas with original image
    if (intensity <= 0 || width === 0 || height === 0) {
        return canvas;
    }

    // Get the image data from the canvas
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data; // This is a Uint8ClampedArray view on the pixel data

    // Normalize intensity to a 0.0 - 1.0 range
    const normalizedIntensity = Math.min(Math.max(intensity, 0), 100) / 100;

    // Effect 1: Horizontal Slice Displacement
    if (normalizedIntensity > 0) {
        const numSlices = Math.floor(normalizedIntensity * 20) + 1; // e.g., 1 to 21 slices
        const maxSliceHeightRatio = 0.1; // Max slice height as a ratio of image height
        
        // Pre-allocate a buffer for one row to avoid repeated allocations in the loop
        const tempRowData = new Uint8ClampedArray(width * 4);

        for (let i = 0; i < numSlices; i++) {
            const sliceY = Math.floor(Math.random() * height);
            // Determine the height of the current slice
            const sliceHeight = Math.max(1, Math.floor(Math.random() * height * maxSliceHeightRatio));
            
            // Determine the horizontal offset for this slice
            // Max offset: up to 15% of width, scaled by intensity, can be left or right
            const maxHorizontalOffset = width * 0.15 * normalizedIntensity;
            const offsetX = Math.floor((Math.random() - 0.5) * 2 * maxHorizontalOffset);

            if (offsetX === 0) continue; // No displacement, skip this slice

            for (let y = sliceY; y < sliceY + sliceHeight && y < height; y++) {
                const rowStartIndex = y * width * 4;
                // Copy original row data for the current row 'y' into tempRowData
                tempRowData.set(data.subarray(rowStartIndex, rowStartIndex + width * 4));

                // Write back shifted row from tempRowData to the main 'data' array
                for (let x = 0; x < width; x++) {
                    const destIdx = (y * width + x) * 4;
                    const srcX = x - offsetX; // Calculate source X after displacement

                    if (srcX >= 0 && srcX < width) {
                        // If source pixel is within bounds, copy it
                        const srcIdxInTemp = srcX * 4;
                        data[destIdx]     = tempRowData[srcIdxInTemp];
                        data[destIdx + 1] = tempRowData[srcIdxInTemp + 1];
                        data[destIdx + 2] = tempRowData[srcIdxInTemp + 2];
                        data[destIdx + 3] = tempRowData[srcIdxInTemp + 3];
                    } else {
                        // If source pixel is out of bounds, fill with transparent black
                        data[destIdx]     = 0;
                        data[destIdx + 1] = 0;
                        data[destIdx + 2] = 0;
                        data[destIdx + 3] = 0; 
                    }
                }
            }
        }
    }
    
    // Effect 2: RGB Channel Shift
    // This effect reads from a snapshot of the data (after slice displacement) 
    // and writes to the main 'data' array to avoid self-interference during channel lookups.
    if (normalizedIntensity > 0) {
        // Create a snapshot of the current pixel data (which includes slice displacement effects)
        const sourceDataForRGBShift = new Uint8ClampedArray(data);
        
        // Max pixel offset for channel shifting, adaptive to image size and intensity
        const maxChannelPixelOffset = Math.floor(Math.min(width, height) * 0.03 * normalizedIntensity);
        // Probability that any given pixel will be affected by channel shift
        const probabilityOfShift = 0.2 * normalizedIntensity; // Max 20% of pixels affected

        if (maxChannelPixelOffset > 0) { // Only proceed if offset can be at least 1 pixel
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    if (Math.random() < probabilityOfShift) {
                        const currentIdx = (y * width + x) * 4;

                        // Random offset for each channel (can be positive or negative)
                        const rOffset = Math.floor((Math.random() * 2 - 1) * maxChannelPixelOffset);
                        const gOffset = Math.floor((Math.random() * 2 - 1) * maxChannelPixelOffset);
                        const bOffset = Math.floor((Math.random() * 2 - 1) * maxChannelPixelOffset);

                        // Calculate source X coordinates for each channel, clamping them to image bounds
                        const rSrcX = Math.max(0, Math.min(width - 1, x + rOffset));
                        const gSrcX = Math.max(0, Math.min(width - 1, x + gOffset));
                        const bSrcX = Math.max(0, Math.min(width - 1, x + bOffset));
                        
                        // Corresponding indices in the source data (Y coordinate remains the same)
                        const rSrcIdx = (y * width + rSrcX) * 4;
                        const gSrcIdx = (y * width + gSrcX) * 4;
                        const bSrcIdx = (y * width + bSrcX) * 4;
                        
                        data[currentIdx]     = sourceDataForRGBShift[rSrcIdx];       // Red from R-shifted source
                        data[currentIdx + 1] = sourceDataForRGBShift[gSrcIdx + 1];   // Green from G-shifted source
                        data[currentIdx + 2] = sourceDataForRGBShift[bSrcIdx + 2];   // Blue from B-shifted source
                        // Alpha of the current pixel is preserved from its state before this effect
                        data[currentIdx + 3] = sourceDataForRGBShift[currentIdx + 3]; 
                    }
                }
            }
        }
    }

    // Effect 3: Scanlines
    // Add scanlines if intensity is reasonably high
    if (normalizedIntensity > 0.1) { 
        const scanlineOpacity = 0.1 * normalizedIntensity; // Max 10% darkening from scanlines
        for (let y = 0; y < height; y += 3) { // Apply to every 3rd row
            for (let x = 0; x < width; x++) {
                const idx = (y * width + x) * 4;
                // Darken R, G, B channels; alpha remains unchanged
                data[idx]   = Math.floor(data[idx]   * (1 - scanlineOpacity));
                data[idx+1] = Math.floor(data[idx+1] * (1 - scanlineOpacity));
                data[idx+2] = Math.floor(data[idx+2] * (1 - scanlineOpacity));
            }
        }
    }
    
    // Put the modified image data back onto the canvas
    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!

Description

The Image Computer Glitch Filter Effect Tool allows users to apply a digital glitch effect to images. By adjusting an intensity parameter, users can create various visual distortions, including horizontal slice displacements, RGB channel shifts, and scanline effects. This tool is ideal for those looking to add a unique, retro-style aesthetic to their photos or digital artwork, making it useful for graphic designers, social media content creators, and anyone interested in enhancing their visuals with an edgy, glitch-inspired look.

Leave a Reply

Your email address will not be published. Required fields are marked *