Please bookmark this page to avoid losing your image tool!

Image Ukiyo-e Japanese Woodblock Print Creator

(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, colorMode = "ukiyo", customPaletteCSV = "", posterizationLevels = 5, edgeThreshold = 70, outlineColor = "#000000") {

    // Helper function: Convert hex to RGB
    // Includes basic validation and default for invalid hex.
    function hexToRgb(hex) {
        if (!hex || typeof hex !== 'string' || !hex.startsWith('#') || (hex.length !== 4 && hex.length !== 7)) {
            // console.warn(`Invalid hex: ${hex}, defaulting to black.`);
            return { r: 0, g: 0, b: 0 }; // Default to black for invalid hex
        }
        let hexVal = hex.slice(1);
        if (hexVal.length === 3) {
            hexVal = hexVal.split('').map(char => char + char).join('');
        }
        
        let bigint = parseInt(hexVal, 16);
        let r = (bigint >> 16) & 255;
        let g = (bigint >> 8) & 255;
        let b = bigint & 255;

        if (isNaN(r) || isNaN(g) || isNaN(b)) {
            // console.warn(`NaN parsing hex: ${hex}, defaulting to black.`);
            return { r: 0, g: 0, b: 0 };
        }
        return { r, g, b };
    }

    // Helper function: Calculate color distance (squared Euclidean distance for efficiency)
    function colorDistanceSquared(rgb1, rgb2) {
        let dr = rgb1.r - rgb2.r;
        let dg = rgb1.g - rgb2.g;
        let db = rgb1.b - rgb2.b;
        return dr * dr + dg * dg + db * db;
    }

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

    if (canvas.width === 0 || canvas.height === 0) {
        console.error("Image has zero dimensions. Cannot process.");
        // Optionally, return a small, identifiable canvas or throw error
        const errCanvas = document.createElement('canvas');
        errCanvas.width = 100; errCanvas.height = 30;
        const errCtx = errCanvas.getContext('2d');
        errCtx.fillStyle = 'red'; errCtx.fillRect(0,0,100,30);
        errCtx.fillStyle = 'white'; errCtx.fillText("Error: Zero dim", 5, 20);
        return errCanvas;
    }

    // Draw original image to a temporary canvas to get its imageData
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    tempCanvas.width = canvas.width;
    tempCanvas.height = canvas.height;
    tempCtx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    const originalImageData = tempCtx.getImageData(0, 0, canvas.width, canvas.height);
    const originalData = originalImageData.data;

    const processedColorImageData = tempCtx.createImageData(canvas.width, canvas.height);
    const processedColorData = processedColorImageData.data;

    // 1. Color Processing
    let activePalette = [];
    let usePosterization = false;
    let localColorMode = colorMode; 

    if (localColorMode === "custom" && customPaletteCSV && customPaletteCSV.trim() !== "") {
        try {
            activePalette = customPaletteCSV.split(',')
                .map(hex => hex.trim())
                .filter(hex => hex.length > 0) 
                .map(hex => hexToRgb(hex));
            
            if (activePalette.some(c => typeof c.r === 'undefined' || isNaN(c.r))) {
                console.warn("Custom palette contains invalid colors. Falling back to 'ukiyo' mode.");
                activePalette = []; 
                localColorMode = "ukiyo";
            } else if (activePalette.length === 0 && customPaletteCSV.trim() !== "") {
                console.warn("Custom palette string was provided but resulted in no valid colors. Falling back to 'ukiyo' mode.");
                localColorMode = "ukiyo";
            }
        } catch (e) {
            console.warn("Error parsing custom palette CSV. Falling back to 'ukiyo' mode.", e);
            activePalette = [];
            localColorMode = "ukiyo";
        }
    }

    if (localColorMode === "ukiyo" && activePalette.length === 0) {
        const defaultUkiyoPaletteSrc = [
            "#2F2F2F", "#F5E9D3", "#E0B7A0", "#B07050", 
            "#4D6A85", "#607850", "#C85050"
        ];
        activePalette = defaultUkiyoPaletteSrc.map(hex => hexToRgb(hex.trim()));
    }
    
    if (activePalette.length === 0) { 
        usePosterization = true;
    }

    for (let i = 0; i < originalData.length; i += 4) {
        let r = originalData[i];
        let g = originalData[i + 1];
        let b = originalData[i + 2];
        const a = originalData[i + 3];

        if (!usePosterization) { // Palette mapping
            let minDistance = Infinity;
            let closestColor = activePalette[0]; 
            for (const palColor of activePalette) {
                const distance = colorDistanceSquared({ r, g, b }, palColor);
                if (distance < minDistance) {
                    minDistance = distance;
                    closestColor = palColor;
                }
            }
            processedColorData[i] = closestColor.r;
            processedColorData[i + 1] = closestColor.g;
            processedColorData[i + 2] = closestColor.b;
        } else { // Posterization
            const levels = Math.max(2, posterizationLevels); 
            const step = 255 / (levels - 1);
            
            processedColorData[i] = Math.max(0, Math.min(255,Math.round(Math.round(r / step) * step)));
            processedColorData[i + 1] = Math.max(0, Math.min(255,Math.round(Math.round(g / step) * step)));
            processedColorData[i + 2] = Math.max(0, Math.min(255,Math.round(Math.round(b / step) * step)));
        }
        processedColorData[i + 3] = a; 
    }
    
    ctx.putImageData(processedColorImageData, 0, 0);

    // 2. Edge Detection (Sobel on grayscale version of original image)
    const grayscaleData = new Uint8ClampedArray(canvas.width * canvas.height);
    for (let i = 0, j = 0; i < originalData.length; i += 4, j++) {
        const r_orig = originalData[i];
        const g_orig = originalData[i + 1];
        const b_orig = originalData[i + 2];
        grayscaleData[j] = Math.round(0.299 * r_orig + 0.587 * g_orig + 0.114 * b_orig);
    }

    const edgeMaskImageData = tempCtx.createImageData(canvas.width, canvas.height);
    const edgeMaskData = edgeMaskImageData.data;
    const { r: outlineR, g: outlineG, b: outlineB } = hexToRgb(outlineColor);

    const Gx = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
    const Gy = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];

    for (let y = 1; y < canvas.height - 1; y++) {
        for (let x = 1; x < canvas.width - 1; x++) {
            let sumX = 0;
            let sumY = 0;

            for (let ky = -1; ky <= 1; ky++) {
                for (let kx = -1; kx <= 1; kx++) {
                    const grayVal = grayscaleData[(y + ky) * canvas.width + (x + kx)];
                    sumX += grayVal * Gx[ky + 1][kx + 1];
                    sumY += grayVal * Gy[ky + 1][kx + 1];
                }
            }

            const magnitude = Math.sqrt(sumX * sumX + sumY * sumY);
            const destIdx = (y * canvas.width + x) * 4;

            if (magnitude > edgeThreshold) {
                edgeMaskData[destIdx] = outlineR;
                edgeMaskData[destIdx + 1] = outlineG;
                edgeMaskData[destIdx + 2] = outlineB;
                edgeMaskData[destIdx + 3] = 255; 
            } else {
                edgeMaskData[destIdx] = 0; 
                edgeMaskData[destIdx + 1] = 0;
                edgeMaskData[destIdx + 2] = 0;
                edgeMaskData[destIdx + 3] = 0;   
            }
        }
    }
    
    const edgeCanvas = document.createElement('canvas');
    edgeCanvas.width = canvas.width;
    edgeCanvas.height = canvas.height;
    const edgeCtx = edgeCanvas.getContext('2d');
    edgeCtx.putImageData(edgeMaskImageData, 0, 0);
    
    ctx.drawImage(edgeCanvas, 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 Ukiyo-e Japanese Woodblock Print Creator transforms your images into a style reminiscent of traditional Japanese woodblock prints. Users can customize the result using a selection of color palettes, including a predefined Ukiyo-e palette or a custom palette via CSV input. This tool supports options for color posterization and edge detection to create a unique artistic effect, making it ideal for artists, designers, and anyone looking to give their images a classic and distinctive appearance. Whether for print, digital art, or educational purposes, this tool offers a creative way to reinterpret photographs and graphics.

Leave a Reply

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

Other Image Tools:

Image Persian Miniature Painting Creator

Image Sci-Fi Movie Poster Template Creator

Image Horror Movie Poster Template

Image Social Media Milestone Certificate Creator

Halloween Death Certificate Template

Image Anatomical Illustration Frame Creator

Image Romance Novel Cover Template Creator

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

Image Birth Certificate Template Generator

Image Driver’s License Template Creator

Image Scout Explorer Badge Template Creator

Image Steampunk Document Frame Creator

Image Vintage Scientific Illustration Frame Creator

Image Magazine Cover Template Creator

See All →