Please bookmark this page to avoid losing your image tool!

Image Dial-up Modem 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, pixelationFactor = 4, colorLevels = 2) {
    // Validate and sanitize parameters
    pixelationFactor = Math.max(1, Number(pixelationFactor) || 4);
    colorLevels = Math.max(2, Number(colorLevels) || 2); // Ensure at least 2 levels for quantization

    const originalWidth = originalImg.naturalWidth;
    const originalHeight = originalImg.naturalHeight;

    // 1. Create a low-resolution canvas for applying the effect
    const effectCanvas = document.createElement('canvas');
    // Ensure scaled dimensions are at least 1x1
    const scaledWidth = Math.max(1, Math.floor(originalWidth / pixelationFactor));
    const scaledHeight = Math.max(1, Math.floor(originalHeight / pixelationFactor));

    effectCanvas.width = scaledWidth;
    effectCanvas.height = scaledHeight;
    const effectCtx = effectCanvas.getContext('2d');

    // Draw the original image scaled down onto the effectCanvas
    // This provides the initial pixelation
    effectCtx.drawImage(originalImg, 0, 0, scaledWidth, scaledHeight);

    // 2. Get pixel data from the low-resolution image
    const imageData = effectCtx.getImageData(0, 0, scaledWidth, scaledHeight);
    const pixels = imageData.data; // This is a Uint8ClampedArray

    // Create a floating-point copy of the pixel data for accurate error accumulation
    // The alpha channel (pixelsFloat[i+3]) is copied but not directly modified by dithering logic.
    const pixelsFloat = new Float32Array(pixels.length);
    for (let i = 0; i < pixels.length; i++) {
        pixelsFloat[i] = pixels[i];
    }

    // Helper function to quantize a single color channel value to a specific number of levels
    function quantizeValue(value, levels) {
        // levels is already guaranteed to be >= 2
        const step = 255 / (levels - 1);
        let quantized = Math.round(value / step) * step;
        return Math.max(0, Math.min(255, quantized)); // Clamp to 0-255 range
    }

    // 3. Apply color quantization and Floyd-Steinberg dithering
    for (let y = 0; y < scaledHeight; y++) {
        for (let x = 0; x < scaledWidth; x++) {
            const i = (y * scaledWidth + x) * 4; // Index for R, G, B, A components

            // Get current color component values (potentially with error diffused from previous pixels)
            const oldR = pixelsFloat[i];
            const oldG = pixelsFloat[i + 1];
            const oldB = pixelsFloat[i + 2];
            // const oldA = pixelsFloat[i + 3]; // Alpha is generally not dithered like color

            // Quantize the color components
            const newR = quantizeValue(oldR, colorLevels);
            const newG = quantizeValue(oldG, colorLevels);
            const newB = quantizeValue(oldB, colorLevels);

            // Apply the quantized color to the actual pixel data (for final output)
            pixels[i]     = newR;
            pixels[i + 1] = newG;
            pixels[i + 2] = newB;
            // Alpha (pixels[i + 3]) remains as it was in the scaled-down image

            // Calculate quantization errors for each color component
            const errR = oldR - newR;
            const errG = oldG - newG;
            const errB = oldB - newB;

            // Distribute the error to neighboring pixels (in the floating-point array)
            // Floyd-Steinberg coefficients:
            //   Pixel to right: 7/16
            //   Pixel bottom-left: 3/16
            //   Pixel bottom: 5/16
            //   Pixel bottom-right: 1/16

            let idx; // Index for neighbor pixel

            // Right pixel: (x+1, y)
            if (x + 1 < scaledWidth) {
                idx = (y * scaledWidth + (x + 1)) * 4;
                pixelsFloat[idx]     += errR * (7 / 16);
                pixelsFloat[idx + 1] += errG * (7 / 16);
                pixelsFloat[idx + 2] += errB * (7 / 16);
            }

            // Bottom-left pixel: (x-1, y+1)
            if (x - 1 >= 0 && y + 1 < scaledHeight) {
                idx = ((y + 1) * scaledWidth + (x - 1)) * 4;
                pixelsFloat[idx]     += errR * (3 / 16);
                pixelsFloat[idx + 1] += errG * (3 / 16);
                pixelsFloat[idx + 2] += errB * (3 / 16);
            }

            // Bottom pixel: (x, y+1)
            if (y + 1 < scaledHeight) {
                idx = ((y + 1) * scaledWidth + x) * 4;
                pixelsFloat[idx]     += errR * (5 / 16);
                pixelsFloat[idx + 1] += errG * (5 / 16);
                pixelsFloat[idx + 2] += errB * (5 / 16);
            }

            // Bottom-right pixel: (x+1, y+1)
            if (x + 1 < scaledWidth && y + 1 < scaledHeight) {
                idx = ((y + 1) * scaledWidth + (x + 1)) * 4;
                pixelsFloat[idx]     += errR * (1 / 16);
                pixelsFloat[idx + 1] += errG * (1 / 16);
                pixelsFloat[idx + 2] += errB * (1 / 16);
            }
        }
    }

    // 4. Put the modified (dithered) pixel data back onto the effectCanvas
    effectCtx.putImageData(imageData, 0, 0);

    // 5. Create the final output canvas, scaled to original dimensions
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = originalWidth;
    outputCanvas.height = originalHeight;
    const outputCtx = outputCanvas.getContext('2d');

    // Disable image smoothing to preserve the pixelated look when scaling up
    outputCtx.imageSmoothingEnabled = false;
    outputCtx.mozImageSmoothingEnabled = false;    // Firefox
    outputCtx.webkitImageSmoothingEnabled = false; // Chrome, Safari, Opera
    outputCtx.msImageSmoothingEnabled = false;     // IE, Edge

    // Draw the dithered effectCanvas (which is small) onto the full-size outputCanvas
    // This scales up the pixelated and dithered image.
    outputCtx.drawImage(effectCanvas, 0, 0, originalWidth, originalHeight);

    return outputCanvas;
}

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 Dial-up Modem Filter Effect Tool allows users to apply a unique visual effect to images that mimics the pixelated aesthetic of low-resolution graphics reminiscent of early dial-up modem connections. This tool features adjustable parameters for pixelation factor and color quantization levels, enabling users to create distinct art styles. It is ideal for graphic designers, retro gaming enthusiasts, and anyone looking to add a nostalgic touch to their digital images by transforming high-resolution pictures into stylized, low-resolution pieces.

Leave a Reply

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