Please bookmark this page to avoid losing your image tool!

Image To Alcohol Marker Converter

(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.
async function processImage(originalImg, simplification = 5, saturation = 1.4, outlineThreshold = 40) {
    const width = originalImg.width;
    const height = originalImg.height;

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    ctx.drawImage(originalImg, 0, 0, width, height);

    const originalImageData = ctx.getImageData(0, 0, width, height);
    const data = originalImageData.data;

    // Helper function to convert RGB to HSL
    const rgbToHsl = (r, g, b) => {
        r /= 255; g /= 255; b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        if (max === min) {
            h = s = 0;
        } else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return [h, s, l];
    };

    // Helper function to convert HSL to RGB
    const hslToRgb = (h, s, l) => {
        let r, g, b;
        if (s === 0) {
            r = g = b = l;
        } else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1 / 6) return p + (q - p) * 6 * t;
                if (t < 1 / 2) return q;
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
                return p;
            };
            const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p = 2 * l - q;
            r = hue2rgb(p, q, h + 1 / 3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1 / 3);
        }
        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    };


    // --- 1. Edge-preserving smoothing (Kuwahara filter) for the marker fill effect ---
    // This is a complex filter that smooths while preserving edges, creating a painterly look.
    const radius = Math.max(1, Math.min(Math.floor(simplification), 15));
    const smoothedData = new Uint8ClampedArray(data.length);
    const get_pixel = (x, y) => (y * width + x) * 4;

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const sums = [[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0]];
            const sqSums = [[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0]];
            const counts = [0, 0, 0, 0];

            for (let j = -radius; j <= radius; j++) {
                for (let i = -radius; i <= radius; i++) {
                    const sampleX = Math.max(0, Math.min(width - 1, x + i));
                    const sampleY = Math.max(0, Math.min(height - 1, y + j));
                    const idx = get_pixel(sampleX, sampleY);
                    const r = data[idx], g = data[idx + 1], b = data[idx + 2];

                    let quadrant = -1;
                    if (i <= 0 && j <= 0) quadrant = 0;      // Top-left
                    else if (i > 0 && j <= 0) quadrant = 1; // Top-right
                    else if (i <= 0 && j > 0) quadrant = 2; // Bottom-left
                    else if (i > 0 && j > 0) quadrant = 3;  // Bottom-right

                    sums[quadrant][0] += r;
                    sums[quadrant][1] += g;
                    sums[quadrant][2] += b;
                    sqSums[quadrant][0] += r * r;
                    sqSums[quadrant][1] += g * g;
                    sqSums[quadrant][2] += b * b;
                    counts[quadrant]++;
                }
            }

            let minVariance = -1;
            let bestQuadrant = -1;
            for (let q = 0; q < 4; q++) {
                if(counts[q] === 0) continue;
                const meanR = sums[q][0] / counts[q];
                const meanG = sums[q][1] / counts[q];
                const meanB = sums[q][2] / counts[q];
                const varianceR = sqSums[q][0] / counts[q] - meanR * meanR;
                const varianceG = sqSums[q][1] / counts[q] - meanG * meanG;
                const varianceB = sqSums[q][2] / counts[q] - meanB * meanB;
                const variance = varianceR + varianceG + varianceB;
                
                if (minVariance === -1 || variance < minVariance) {
                    minVariance = variance;
                    bestQuadrant = q;
                }
            }

            const outIdx = get_pixel(x, y);
            smoothedData[outIdx] = sums[bestQuadrant][0] / counts[bestQuadrant];
            smoothedData[outIdx + 1] = sums[bestQuadrant][1] / counts[bestQuadrant];
            smoothedData[outIdx + 2] = sums[bestQuadrant][2] / counts[bestQuadrant];
            smoothedData[outIdx + 3] = 255;
        }
    }
    
    // --- 2. Increase saturation for vibrant marker colors ---
    for (let i = 0; i < smoothedData.length; i += 4) {
        let [h, s, l] = rgbToHsl(smoothedData[i], smoothedData[i+1], smoothedData[i+2]);
        s = Math.min(1, s * saturation);
        const [r, g, b] = hslToRgb(h, s, l);
        smoothedData[i] = r;
        smoothedData[i + 1] = g;
        smoothedData[i + 2] = b;
    }
    
    // --- 3. Edge detection (Sobel) for marker outlines ---
    const grayscaleData = new Uint8ClampedArray(width * height);
    for (let i = 0, j = 0; i < data.length; i += 4, j++) {
        grayscaleData[j] = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
    }

    const outlineData = new Uint8ClampedArray(data.length).fill(0);
    const sobelX = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
    const sobelY = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];

    for (let y = 1; y < height - 1; y++) {
        for (let x = 1; x < width - 1; x++) {
            let pixelX = 0, pixelY = 0;
            for (let j = -1; j <= 1; j++) {
                for (let i = -1; i <= 1; i++) {
                    const grayValue = grayscaleData[(y + j) * width + (x + i)];
                    pixelX += grayValue * sobelX[j + 1][i + 1];
                    pixelY += grayValue * sobelY[j + 1][i + 1];
                }
            }
            const magnitude = Math.sqrt(pixelX * pixelX + pixelY * pixelY);
            if (magnitude > outlineThreshold) {
                const idx = (y * width + x) * 4;
                outlineData[idx] = 0; // R
                outlineData[idx + 1] = 0; // G
                outlineData[idx + 2] = 0; // B
                outlineData[idx + 3] = 120 + Math.min(135, magnitude); // Alpha based on strength
            }
        }
    }

    // --- 4. Composite the layers ---
    // Place the smoothed and saturated base image
    ctx.putImageData(new ImageData(smoothedData, width, height), 0, 0);

    // Create a temporary canvas for the outlines
    const outlineCanvas = document.createElement('canvas');
    outlineCanvas.width = width;
    outlineCanvas.height = height;
    const outlineCtx = outlineCanvas.getContext('2d');
    outlineCtx.putImageData(new ImageData(outlineData, width, height), 0, 0);

    // Draw the outlines on top of the base image
    ctx.drawImage(outlineCanvas, 0, 0);

    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

The Image To Alcohol Marker Converter is a tool designed to transform standard images into a stylized representation that mimics the look of artwork created with alcohol-based markers. This tool enhances images by applying an edge-preserving smoothing effect, increasing color saturation for vibrant outputs, and adding outlines for definition. Users can adjust parameters such as simplification, saturation, and outline thresholds to achieve the desired artistic effect. This tool is useful for artists, designers, and hobbyists looking to create visually engaging content, illustrations, or mockups for presentations and projects.

Leave a Reply

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