Please bookmark this page to avoid losing your image tool!

Image Charcoal 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, strokeWidth = 5, contrast = 1.0) {
    const W = originalImg.naturalWidth || originalImg.width;
    const H = originalImg.naturalHeight || originalImg.height;

    // 1. Create grayscale version of the image (baseGrayData)
    // This canvas is used to draw the original image and get its pixel data.
    const canvas1 = document.createElement('canvas');
    canvas1.width = W;
    canvas1.height = H;
    const ctx1 = canvas1.getContext('2d', { willReadFrequently: true });
    ctx1.drawImage(originalImg, 0, 0, W, H);
    const imageData1 = ctx1.getImageData(0, 0, W, H);
    const baseGrayData = imageData1.data; // This is an Uint8ClampedArray view on imageData1.buffer

    // Convert to grayscale in place
    for (let i = 0; i < baseGrayData.length; i += 4) {
        const r = baseGrayData[i];
        const g = baseGrayData[i + 1];
        const b = baseGrayData[i + 2];
        // Using NTSC weighting for grayscale conversion
        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
        baseGrayData[i] = gray;
        baseGrayData[i + 1] = gray;
        baseGrayData[i + 2] = gray;
    }
    // baseGrayData (imageData1.data) is now modified with grayscale values.

    // 2. Create inverted and blurred version of the grayscale image (blendLayerData)
    // First, create inverted grayscale data from baseGrayData
    const invertedGrayArray = new Uint8ClampedArray(baseGrayData.length);
    for (let i = 0; i < baseGrayData.length; i += 4) {
        const gray = baseGrayData[i];
        invertedGrayArray[i] = 255 - gray;
        invertedGrayArray[i + 1] = 255 - gray;
        invertedGrayArray[i + 2] = 255 - gray;
        invertedGrayArray[i + 3] = baseGrayData[i + 3]; // Preserve alpha
    }
    const invertedImageData = new ImageData(invertedGrayArray, W, H);

    // Canvas to hold the inverted image, which will be the source for blurring
    const canvas2 = document.createElement('canvas');
    canvas2.width = W;
    canvas2.height = H;
    const ctx2 = canvas2.getContext('2d', { willReadFrequently: true });
    ctx2.putImageData(invertedImageData, 0, 0);

    // Canvas to draw the blurred version of the inverted image
    const tempBlurCanvas = document.createElement('canvas');
    tempBlurCanvas.width = W;
    tempBlurCanvas.height = H;
    const tempBlurCtx = tempBlurCanvas.getContext('2d', { willReadFrequently: true });

    // Apply blur. ctx.filter = 'blur(0px)' results in no blur.
    // strokeWidth controls the blur radius.
    if (strokeWidth > 0) {
        tempBlurCtx.filter = `blur(${strokeWidth}px)`;
    }
    tempBlurCtx.drawImage(canvas2, 0, 0, W, H); // Draw inverted image (canvas2) onto tempBlurCanvas with blur

    const blurredInvertedImageData = tempBlurCtx.getImageData(0, 0, W, H);
    const blendLayerData = blurredInvertedImageData.data;

    // 3. Blend using Color Dodge formula
    // 4. Apply Contrast adjustment
    const finalCanvas = document.createElement('canvas');
    finalCanvas.width = W;
    finalCanvas.height = H;
    const finalCtx = finalCanvas.getContext('2d');
    const finalImageData = finalCtx.createImageData(W, H);
    const finalData = finalImageData.data;

    for (let i = 0; i < finalData.length; i += 4) {
        const basePixelVal = baseGrayData[i];    // From original grayscale image
        const blendPixelVal = blendLayerData[i]; // From blurred-inverted-grayscale image

        let dodgeResult;
        if (blendPixelVal === 255) { // If blend layer is white, result is white
            dodgeResult = 255;
        } else if (basePixelVal === 0) { // If base layer is black (and blend is not white), result is black
            dodgeResult = 0;
        } else {
            // Color Dodge formula: Result = Base / (1 - Blend_normalized)
            // For byte values: Result = (Base * 255) / (255 - Blend)
            dodgeResult = (basePixelVal * 255.0) / (255.0 - blendPixelVal);
        }
        
        // Clamp dodgeResult to [0, 255] as it can exceed 255
        dodgeResult = Math.min(255, Math.max(0, dodgeResult));

        // Apply contrast adjustment
        let finalValue = dodgeResult;
        if (contrast !== 1.0) { // Avoid calculation if contrast is neutral (1.0)
            // Standard contrast formula: val = (val/255 - 0.5) * contrast + 0.5; val *= 255
            finalValue = (finalValue / 255.0 - 0.5) * contrast + 0.5;
            finalValue *= 255.0;
        }
        
        // Clamp finalValue again after contrast, to [0, 255]
        finalValue = Math.min(255, Math.max(0, finalValue));

        finalData[i] = finalValue;
        finalData[i + 1] = finalValue;
        finalData[i + 2] = finalValue;
        finalData[i + 3] = baseGrayData[i + 3]; // Preserve original alpha
    }

    finalCtx.putImageData(finalImageData, 0, 0);
    return finalCanvas;
}

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 Charcoal Filter Application allows users to transform images into a stylized charcoal drawing effect. By converting images to grayscale and applying a blend of inverted and blurred layers, users can achieve a unique artistic appearance. This tool can be useful for graphic designers, artists, or anyone looking to enhance their images with a creative touch, whether for social media, artwork, or personal projects.

Leave a Reply

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