Please bookmark this page to avoid losing your image tool!

Image Dot Matrix Filter

(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, cellSize = 8, dotColor = "black", backgroundColor = "white", intensityMapping = "darkerIsLarger", dotScale = 0.9) {
    const originalWidth = originalImg.naturalWidth || originalImg.width;
    const originalHeight = originalImg.naturalHeight || originalImg.height;

    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = originalWidth;
    outputCanvas.height = originalHeight;

    // Ensure cellSize is a positive integer.
    const effectiveCellSize = Math.max(1, Math.floor(cellSize));

    // Handle cases where the image might be empty or not loaded yet.
    if (originalWidth === 0 || originalHeight === 0) {
        try {
            const ctxCheck = outputCanvas.getContext('2d');
            if (ctxCheck) {
                // If dimensions are >0 (e.g., from attributes) but image data is missing, fill with background.
                // If 0x0, this will likely be a no-op.
                ctxCheck.fillStyle = backgroundColor;
                ctxCheck.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
            }
        } catch (e) {
            // Log warning if context operations fail (e.g. on 0x0 canvas in some environments)
            console.warn("Canvas context error on 0-dimension image:", e);
        }
        return outputCanvas; // Return canvas (possibly 0x0 or background-filled)
    }
    
    const ctx = outputCanvas.getContext('2d');
    if (!ctx) {
        // This should ideally not happen if dimensions are valid.
        console.error("Failed to get 2D context for output canvas.");
        return outputCanvas; // Return the canvas, though it might be unusable.
    }

    // Fill the output canvas with the specified background color.
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);

    // Create a temporary offscreen canvas to draw the original image.
    // This allows us to reliably get pixel data using getImageData.
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = originalWidth;
    tempCanvas.height = originalHeight;
    const tempCtx = tempCanvas.getContext('2d');
    
    if (!tempCtx) { 
        console.error("Failed to get 2D context for temporary canvas.");
        // If temp canvas fails, return the background-filled output canvas.
        return outputCanvas; 
    }
    
    try {
        // Draw the original image onto the temporary canvas.
        tempCtx.drawImage(originalImg, 0, 0, originalWidth, originalHeight);
    } catch (e) {
        console.error("Error drawing original image to temporary canvas:", e);
        // If drawing fails (e.g., for security reasons with some image sources),
        // return the background-filled output canvas.
        return outputCanvas;
    }

    // Ensure dotScale is not negative; it represents a scale factor.
    const effectiveDotScale = Math.max(0, dotScale); 

    // Iterate over the image in a grid of cells.
    for (let y = 0; y < originalHeight; y += effectiveCellSize) {
        for (let x = 0; x < originalWidth; x += effectiveCellSize) {
            // Define the rectangular region in the original image to sample for the current cell.
            const sampleX = x;
            const sampleY = y;
            // Adjust sample dimensions for cells at the image edges to stay within bounds.
            const sampleWidth = Math.min(effectiveCellSize, originalWidth - x);
            const sampleHeight = Math.min(effectiveCellSize, originalHeight - y);

            // Skip if the calculated sample area is invalid or has zero dimension.
            if (sampleWidth <= 0 || sampleHeight <= 0) {
                continue;
            }

            let imageData;
            try {
                // Extract pixel data from the sampled region of the temporary canvas.
                imageData = tempCtx.getImageData(sampleX, sampleY, sampleWidth, sampleHeight);
            } catch (e) {
                // This error can occur if the canvas becomes tainted (e.g., by a cross-origin image without CORS headers).
                console.error("Failed to get ImageData (CORS issue or other security restriction?):", e);
                // Skip processing this block if pixel data cannot be accessed.
                continue; 
            }
            
            const data = imageData.data;
            let totalLuminance = 0;
            // imageData.width and imageData.height reflect the actual dimensions of the extracted data.
            const numPixelsInBlock = imageData.width * imageData.height;

            // If the block contains no pixels, skip it.
            if (numPixelsInBlock === 0) continue;

            // Calculate the average luminance of the pixels in the block.
            for (let i = 0; i < data.length; i += 4) {
                const r = data[i];    // Red
                const g = data[i+1];  // Green
                const b = data[i+2];  // Blue
                // Alpha (data[i+3]) is not used in luminance calculation here.
                // Standard formula for luminance (perceived brightness).
                const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
                totalLuminance += luminance;
            }

            // Average luminance for the block, normalized to a 0 (black) to 1 (white) scale.
            const averageLuminance = totalLuminance / numPixelsInBlock;
            const normalizedLuminance = averageLuminance / 255;

            let dotRadiusFactor;
            // Determine how dot size scales with image intensity.
            if (intensityMapping === "darkerIsLarger") {
                dotRadiusFactor = 1 - normalizedLuminance; // Darker areas -> larger factor (bigger dot)
            } else { // Default or "brighterIsLarger"
                dotRadiusFactor = normalizedLuminance;    // Brighter areas -> larger factor (bigger dot)
            }
            
            // The maximum possible radius for a dot is half the cell size.
            const maxRadiusInCell = effectiveCellSize / 2;
            // Calculate the actual radius for the current dot based on luminance and scaling factors.
            const currentDotRadius = maxRadiusInCell * dotRadiusFactor * effectiveDotScale;

            // Only draw the dot if its radius is large enough to be visible.
            // This also prevents attempting to draw circles with zero or negative radius.
            if (currentDotRadius > 0.1) { 
                // Dots are drawn centered within their respective grid cell.
                const centerX = x + effectiveCellSize / 2;
                const centerY = y + effectiveCellSize / 2;

                ctx.beginPath();
                ctx.arc(centerX, centerY, currentDotRadius, 0, 2 * Math.PI, false);
                ctx.fillStyle = dotColor;
                ctx.fill();
            }
        }
    }

    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 Dot Matrix Filter is a tool that transforms images into a dot matrix representation, creating a visual effect reminiscent of printed media or retro graphics. Users can customize parameters such as dot size, color, and background, as well as how the luminance of the original image influences the size of the dots. This tool can be utilized for artistic projects, graphic design, and creating unique visual styles for presentations or social media content.

Leave a Reply

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