Please bookmark this page to avoid losing your image tool!

Image Impressionist Filter Application

(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, brushSize_param = 10, strokeDensity_param = 1.0, strokeOpacity_param = 0.7, jitter_param = 0.5) {
    // Validate and clamp parameters
    const brushSize = Math.max(3, Number(brushSize_param)); // Min brush size 3 for performance & aesthetics
    const strokeDensity = Math.max(0.01, Number(strokeDensity_param)); // Min density to ensure some strokes
    const strokeOpacity = Math.max(0.01, Math.min(1, Number(strokeOpacity_param)));
    const jitter = Math.max(0, Math.min(1, Number(jitter_param)));

    const outputCanvas = document.createElement('canvas');
    const outputCtx = outputCanvas.getContext('2d');
    
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    if (width === 0 || height === 0) {
        // Handle unloaded or zero-size image case
        outputCanvas.width = 1;
        outputCanvas.height = 1;
        outputCtx.fillStyle = 'white';
        outputCtx.fillRect(0, 0, 1, 1);
        console.warn("Impressionist filter: Original image has zero width or height.");
        return outputCanvas;
    }

    outputCanvas.width = width;
    outputCanvas.height = height;

    // Fill background. Impressionist paintings are often on a light gesso layer.
    outputCtx.fillStyle = 'white';
    outputCtx.fillRect(0, 0, width, height);

    // Create a source canvas to get pixel data from the original image.
    // This is necessary to avoid issues with getImageData on potentially transformed/scaled display images
    // and to handle cross-origin issues if the image is not locally hosted or CORS-enabled.
    const sourceCanvas = document.createElement('canvas');
    sourceCanvas.width = width;
    sourceCanvas.height = height;
    const sourceCtx = sourceCanvas.getContext('2d');
    
    let imageData;
    try {
        sourceCtx.drawImage(originalImg, 0, 0, width, height);
        imageData = sourceCtx.getImageData(0, 0, width, height);
    } catch (e) {
        // Handle potential security exceptions (e.g., tainted canvas from cross-origin image)
        console.error("Impressionist filter: Could not get image data, possibly due to cross-origin restrictions.", e);
        // Fallback: draw original image with an error message overlay
        outputCtx.drawImage(originalImg, 0, 0, width, height);
        outputCtx.fillStyle = 'rgba(255, 0, 0, 0.3)';
        outputCtx.fillRect(0, 0, width, height);
        outputCtx.fillStyle = 'white';
        outputCtx.strokeStyle = 'black';
        outputCtx.lineWidth = 2;
        outputCtx.font = 'bold 16px Arial';
        outputCtx.textAlign = 'center';
        const errorMessage = 'Error: Cannot apply filter due to image security restrictions.';
        outputCtx.strokeText(errorMessage, width / 2, height / 2);
        outputCtx.fillText(errorMessage, width / 2, height / 2);
        return outputCanvas;
    }
    const pixels = imageData.data;

    // Calculate the number of strokes to draw.
    // This is based on the image area, desired brush size, and density.
    // A higher density means more strokes per unit area.
    const numStrokes = Math.floor((width * height / (brushSize * brushSize)) * strokeDensity);
    
    outputCtx.globalAlpha = strokeOpacity;

    for (let i = 0; i < numStrokes; i++) {
        // Pick a random point in the image to sample color from.
        // This point also serves as the base for the stroke's center.
        const sampleX = Math.random() * width;
        const sampleY = Math.random() * height;

        // Get the color of the pixel at (sampleX, sampleY).
        // Ensure pixel coordinates are within image bounds.
        const pixelX = Math.min(Math.floor(sampleX), width - 1);
        const pixelY = Math.min(Math.floor(sampleY), height - 1);
        const dataIndex = (pixelY * width + pixelX) * 4;
        
        const r = pixels[dataIndex];
        const g = pixels[dataIndex + 1];
        const b = pixels[dataIndex + 2];
        // const a = pixels[dataIndex + 3]; // Original alpha, not typically used for stroke color directly

        outputCtx.fillStyle = `rgb(${r},${g},${b})`;

        // Calculate stroke properties, applying jitter for randomness.
        // Stroke center: slightly offset from the sample point.
        const strokeCenterX = sampleX + (Math.random() - 0.5) * brushSize * jitter;
        const strokeCenterY = sampleY + (Math.random() - 0.5) * brushSize * jitter;

        // Base size for this stroke: varied by jitter.
        // (Math.random() - 0.5) gives a range of [-0.5, 0.5).
        // So, size varies from brushSize*(1-jitter/2) to brushSize*(1+jitter/2).
        const currentBaseSize = brushSize * (1 + (Math.random() - 0.5) * jitter);

        // Ellipse radii: allow for eccentric shapes. Varied independently.
        // The 0.8 factor scales the jitter's effect on eccentricity.
        const radiusX = Math.max(1, currentBaseSize * (1 + (Math.random() - 0.5) * jitter * 0.8));
        const radiusY = Math.max(1, currentBaseSize * (1 + (Math.random() - 0.5) * jitter * 0.8));
        
        // Rotation of the ellipse: scaled by jitter.
        // If jitter is 0, rotation is 0. If jitter is 1, full random rotation.
        const rotation = Math.random() * Math.PI * 2 * jitter;

        // Draw the "brush stroke" (an ellipse in this case).
        outputCtx.beginPath();
        outputCtx.ellipse(strokeCenterX, strokeCenterY, radiusX, radiusY, rotation, 0, 2 * Math.PI);
        outputCtx.fill();
    }

    // Reset globalAlpha to default, important if context is reused.
    outputCtx.globalAlpha = 1.0;

    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 Impressionist Filter Application allows users to transform their images into an impressionist style artwork. By adjusting parameters such as brush size, stroke density, stroke opacity, and jitter, users can create unique artistic effects that mimic the style of brush strokes typical of impressionist paintings. This tool can be useful for artists, designers, or anyone looking to add a creative flair to their images, making it suitable for digital art projects, social media content, or personalized gifts.

Leave a Reply

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