Please bookmark this page to avoid losing your image tool!

Image To Shading 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.
/**
 * Converts an image into a monochrome shading (hatching and cross-hatching) representation.
 * The density and darkness of the lines correspond to the brightness of the original image.
 *
 * @param {HTMLImageElement} originalImg The original image element to process.
 * @param {number} lineDensity The spacing between line origins. Smaller values create a denser, darker, and more detailed image. A value between 4 and 12 is recommended.
 * @param {number} lineThickness The thickness of the individual shading lines.
 * @param {string} lineColor A CSS color string for the shading lines (e.g., 'black', '#333').
 * @param {string} backgroundColor A CSS color string for the background of the output.
 * @param {string} crossHatchThreshold The darkness threshold (a string from '0.0' to '1.0') above which cross-hatching is applied. A value of '1.0' effectively disables cross-hatching.
 * @returns {HTMLCanvasElement} A new canvas element with the shaded image drawn on it.
 */
function processImage(originalImg, lineDensity = 8, lineThickness = 1, lineColor = 'black', backgroundColor = 'white', crossHatchThreshold = '0.7') {
    // 1. Sanitize and prepare parameters
    const density = Math.max(1, Number(lineDensity));
    const thickness = Math.max(0.1, Number(lineThickness));
    const threshold = Math.max(0.0, Math.min(1.0, Number(crossHatchThreshold)));
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;

    // 2. Create an offscreen canvas to get pixel data from the original image.
    // This avoids affecting the original image object.
    const offscreenCanvas = document.createElement('canvas');
    offscreenCanvas.width = width;
    offscreenCanvas.height = height;
    const offscreenCtx = offscreenCanvas.getContext('2d', {
        willReadFrequently: true
    });
    offscreenCtx.drawImage(originalImg, 0, 0);
    const imageData = offscreenCtx.getImageData(0, 0, width, height).data;

    // 3. Create the visible output canvas
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const ctx = outputCanvas.getContext('2d');

    // 4. Set up the canvas style
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, width, height);
    ctx.strokeStyle = lineColor;
    ctx.lineWidth = thickness;
    ctx.lineCap = 'round'; // Makes line ends look slightly softer

    // Pre-calculate constants for 45-degree angle calculations
    const cos45 = Math.cos(Math.PI / 4);
    const sin45 = Math.sin(Math.PI / 4);

    // 5. Iterate over the image in a grid defined by the `density` parameter
    for (let y = 0; y < height; y += density) {
        for (let x = 0; x < width; x += density) {
            // Get the average brightness of the pixels within the current grid cell
            // for a smoother, more accurate result than sampling a single pixel.
            let totalBrightness = 0;
            let count = 0;
            for (let subY = y; subY < y + density && subY < height; subY++) {
                for (let subX = x; subX < x + density && subX < width; subX++) {
                    const idx = (subY * width + subX) * 4;
                    const r = imageData[idx];
                    const g = imageData[idx + 1];
                    const b = imageData[idx + 2];
                    // Standard NTSC luminance calculation
                    totalBrightness += (r * 0.299 + g * 0.587 + b * 0.114);
                    count++;
                }
            }

            if (count === 0) continue;
            const avgBrightness = totalBrightness / count;

            // Convert brightness (0-255) to a darkness factor (0.0 for white, 1.0 for black)
            const darknessFactor = (255 - avgBrightness) / 255;

            // Skip drawing lines for very bright areas to keep highlights clean
            if (darknessFactor < 0.1) {
                continue;
            }

            // Calculate the shading line's length based on the darkness of the area.
            // Allow lines to slightly exceed the cell diagonal for better, more organic coverage.
            const maxLineLength = density * 1.5;
            const lineLength = darknessFactor * maxLineLength;
            const halfLength = lineLength / 2;

            // Center the line within the current grid cell
            const centerX = x + density / 2;
            const centerY = y + density / 2;

            // Draw the primary hatching line (at -45 degrees)
            let offsetX = halfLength * cos45;
            let offsetY = halfLength * sin45;

            ctx.beginPath();
            ctx.moveTo(centerX - offsetX, centerY + offsetY);
            ctx.lineTo(centerX + offsetX, centerY - offsetY);
            ctx.stroke();

            // If the area is dark enough (exceeds the threshold), a a cross-hatching line.
            if (darknessFactor > threshold) {
                // The length of the second line is proportional to how much the darkness exceeds the threshold.
                // This creates a smooth transition into cross-hatched areas.
                const crossLineLength = ((darknessFactor - threshold) / (1.0 - threshold)) * maxLineLength;
                const crossHalfLength = crossLineLength / 2;

                offsetX = crossHalfLength * cos45;
                offsetY = crossHalfLength * sin45;
                
                // Draw the cross-hatching line (at +45 degrees)
                ctx.beginPath();
                ctx.moveTo(centerX - offsetX, centerY - offsetY);
                ctx.lineTo(centerX + offsetX, centerY + offsetY);
                ctx.stroke();
            }
        }
    }

    // 6. Return the final canvas element
    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 To Shading Converter is a tool that transforms images into a monochrome shading representation using hatching and cross-hatching techniques. Users can adjust the line density, thickness, and colors to fine-tune the output, allowing for creative expression in art and design. This tool is ideal for artists looking to create unique visual interpretations of photos, for educators needing illustrative materials, or for anyone wanting to add a stylish shading effect to their images.

Leave a Reply

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