Please bookmark this page to avoid losing your image tool!

True Artist Image Editor

(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, style = 'impressionist', intensity = 5) {
    const width = originalImg.width;
    const height = originalImg.height;

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    // Make sure intensity is reasonable
    intensity = Math.max(1, intensity);

    if (style === 'sketch') {
        // --- 1. Pencil Sketch Effect ---
        
        // Draw the grayscale base
        ctx.filter = 'grayscale(100%)';
        ctx.drawImage(originalImg, 0, 0, width, height);

        // Create the inverted blurred layer
        const blurCanvas = document.createElement('canvas');
        blurCanvas.width = width;
        blurCanvas.height = height;
        const bctx = blurCanvas.getContext('2d');
        
        bctx.filter = `grayscale(100%) invert(100%) blur(${intensity}px)`;
        bctx.drawImage(originalImg, 0, 0, width, height);

        // Blend with Color Dodge to get the pencil lines
        ctx.globalCompositeOperation = 'color-dodge';
        ctx.drawImage(blurCanvas, 0, 0, width, height);

        // To prevent washed-out sketch lines, multiply the canvas over itself
        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = width;
        tempCanvas.height = height;
        tempCanvas.getContext('2d').drawImage(canvas, 0, 0);

        ctx.globalCompositeOperation = 'multiply';
        ctx.globalAlpha = 0.6; // Deepens the contrast of the sketch lines
        ctx.filter = 'none';
        ctx.drawImage(tempCanvas, 0, 0, width, height);
        
        // Reset properties
        ctx.globalAlpha = 1.0;
        ctx.globalCompositeOperation = 'source-over';

        return canvas;
    } 
    else if (style === 'watercolor') {
        // --- 2. Ink and Wash (Watercolor) Effect ---
        
        // Base canvas heavily saturates and slightly blurs colors
        const baseCanvas = document.createElement('canvas');
        baseCanvas.width = width;
        baseCanvas.height = height;
        const bctx = baseCanvas.getContext('2d');

        bctx.filter = `saturate(150%) blur(${Math.max(1, intensity)}px) contrast(110%)`;
        bctx.drawImage(originalImg, 0, 0, width, height);
        ctx.drawImage(baseCanvas, 0, 0, width, height);

        // Introduce high contrast grayscale edges over it
        ctx.globalCompositeOperation = 'multiply';
        ctx.globalAlpha = 0.6;
        ctx.filter = `blur(${Math.max(1, intensity / 2)}px) grayscale(100%) contrast(200%)`;
        ctx.drawImage(originalImg, 0, 0, width, height);

        ctx.globalAlpha = 1.0;
        ctx.globalCompositeOperation = 'source-over';
        ctx.filter = 'none';

        return canvas;
    }

    // --- 3. Impressionist Painter (Default "True Artist" Effect) ---
    
    // Fill background with a blurred version of the image to ensure complete coverage seamlessly
    ctx.filter = `blur(${intensity * 2}px)`;
    ctx.drawImage(originalImg, 0, 0, width, height);
    ctx.filter = 'none';

    // Set up unblurred image data for sampling pure color and gradient edges
    const origCanvas = document.createElement('canvas');
    origCanvas.width = width;
    origCanvas.height = height;
    const octx = origCanvas.getContext('2d');
    octx.drawImage(originalImg, 0, 0, width, height);

    const imgData = octx.getImageData(0, 0, width, height);
    const data = imgData.data;

    // Cache Luminosity to easily perform edge detection calculations
    const luma = new Float32Array(width * height);
    for (let i = 0; i < width * height; i++) {
        luma[i] = 0.299 * data[i * 4] + 0.587 * data[i * 4 + 1] + 0.114 * data[i * 4 + 2];
    }

    function getLuma(x, y) {
        x = Math.max(0, Math.min(x, width - 1));
        y = Math.max(0, Math.min(y, height - 1));
        return luma[y * width + x];
    }

    const brushSize = intensity;
    const strokeLength = intensity * 3;
    // Calculate total brush strokes ensuring proportional coverage of the canvas
    const numStrokes = Math.floor((width * height) / (brushSize * brushSize)) * 2;

    ctx.lineCap = 'round';

    // The artist paints the canvas!
    for (let i = 0; i < numStrokes; i++) {
        const x = Math.floor(Math.random() * width);
        const y = Math.floor(Math.random() * height);

        // Perform basic Sobel operator for orientation mapping
        const tl = getLuma(x - 1, y - 1);
        const tc = getLuma(x, y - 1);
        const tr = getLuma(x + 1, y - 1);
        const cl = getLuma(x - 1, y);
        const cr = getLuma(x + 1, y);
        const bl = getLuma(x - 1, y + 1);
        const bc = getLuma(x, y + 1);
        const br = getLuma(x + 1, y + 1);

        const gx = -tl - 2 * cl - bl + tr + 2 * cr + br;
        const gy = -tl - 2 * tc - tr + bl + 2 * bc + br;
        
        const edgeMagnitude = Math.sqrt(gx * gx + gy * gy);
        let angle;

        // If in a smooth, flat area, paint with chaotic strokes. 
        // If bordering an edge, strictly follow the orientation of the shape.
        if (edgeMagnitude < 15) {
            angle = Math.random() * Math.PI * 2;
        } else {
            angle = Math.atan2(gy, gx) + Math.PI / 2;
        }

        const progress = i / numStrokes;
        // Natural jitter for the human hand element
        angle += (Math.random() - 0.5) * (0.2 + progress * 0.5);

        const idx = (y * width + x) * 4;
        const r = data[idx];
        const g = data[idx + 1];
        const b = data[idx + 2];

        // Artist starts with big broad strokes and finishes the top layer with fine detailing
        const scaleFactor = 2.4 - progress * 2.0;

        const currentBrushSize = brushSize * scaleFactor * (0.8 + Math.random() * 0.4);
        const currentStrokeLength = strokeLength * scaleFactor * (0.8 + Math.random() * 0.4);

        ctx.lineWidth = currentBrushSize;
        // Paint mixing properties via minor opacity tweaks per stroke
        ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${0.7 + Math.random() * 0.3})`;
        const halfLen = currentStrokeLength / 2;

        ctx.beginPath();
        ctx.moveTo(x - Math.cos(angle) * halfLen, y - Math.sin(angle) * halfLen);
        ctx.lineTo(x + Math.cos(angle) * halfLen, y + Math.sin(angle) * halfLen);
        ctx.stroke();
    }

    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

True Artist Image Editor is an image processing tool that applies various artistic styles to your photos. It can transform images into detailed pencil sketches, soft watercolor paintings, or impressionist-style artworks using simulated brush strokes that adapt to the edges and colors of your original photo. This tool is ideal for creative social media posts, making personalized digital art, or adding an aesthetic, handcrafted feel to everyday photographs.

Leave a Reply

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