Please bookmark this page to avoid losing your image tool!

Image Halftone Effect Applicator

(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.
/**
 * Applies a CMYK halftone effect to an image, simulating the look of comic books or old newspaper prints.
 * This is achieved by converting the image to the CMYK color space and then representing each
 * color channel (Cyan, Magenta, Yellow, Black) as a grid of dots at different angles.
 * The size of the dots varies with the intensity of the channel's color.
 *
 * @param {Image} originalImg The original HTML Image object to process.
 * @param {number} spacing The base spacing between the centers of the dots in the halftone grid. Default is 5.
 * @param {number} dotScale A multiplier to adjust the overall size of the dots. Values around 1.0 are standard. Default is 1.0.
 * @param {number} kAngle The screen angle in degrees for the black (Key) channel. The angles for Cyan, Magenta, and Yellow are calculated relative to this for a classic halftone pattern. Default is 45.
 * @returns {HTMLCanvasElement} A new canvas element displaying the processed image with the halftone effect.
 */
async function processImage(originalImg, spacing = 5, dotScale = 1.0, kAngle = 45) {
    const width = originalImg.width;
    const height = originalImg.height;

    // Create the destination canvas, which will be returned
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    // Start with a white background, essential for the color blending
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, width, height);

    // Get the pixel data from the original image for color sampling
    const srcCanvas = document.createElement('canvas');
    srcCanvas.width = width;
    srcCanvas.height = height;
    const srcCtx = srcCanvas.getContext('2d', {
        willReadFrequently: true
    });
    srcCtx.drawImage(originalImg, 0, 0);
    const imageData = srcCtx.getImageData(0, 0, width, height).data;

    // Helper function to convert an RGB color to CMYK values (ranging from 0 to 1)
    function rgbToCmyk(r, g, b) {
        let c = 1 - (r / 255);
        let m = 1 - (g / 255);
        let y = 1 - (b / 255);
        let k = Math.min(c, m, y);

        if (k === 1) { // Black
            return [0, 0, 0, 1];
        }

        // CMY values are corrected against the black component
        c = (c - k) / (1 - k);
        m = (m - k) / (1 - k);
        y = (y - k) / (1 - k);

        return [c, m, y, k];
    }

    // Define properties for each CMYK channel using classic screen angles
    const channels = [{
        color: 'cyan',
        angle: (kAngle + 15)
    }, {
        color: 'magenta',
        angle: (kAngle + 75)
    }, {
        color: 'yellow',
        angle: (kAngle + 0)
    }, {
        color: 'black',
        angle: (kAngle + 45)
    }];

    // Use 'darken' to blend the CMYK layers, simulating ink on paper
    ctx.globalCompositeOperation = 'darken';

    // Process and draw each channel's halftone screen
    for (let i = 0; i < channels.length; i++) {
        const channel = channels[i];
        const angleRad = channel.angle * Math.PI / 180;

        ctx.save();

        // Rotate the entire canvas to match the channel's screen angle
        ctx.translate(width / 2, height / 2);
        ctx.rotate(angleRad);
        ctx.translate(-width / 2, -height / 2);

        ctx.fillStyle = channel.color;

        // Calculate inverse rotation matrix to map canvas points back to original image coordinates
        const cos = Math.cos(-angleRad);
        const sin = Math.sin(-angleRad);
        
        // Loop over a grid on the rotated canvas. The grid must be large enough
        // to cover the original image area after rotation.
        const diagonal = Math.sqrt(width * width + height * height);
        const startX = (width - diagonal) / 2;
        const startY = (height - diagonal) / 2;
        const endX = startX + diagonal;
        const endY = startY + diagonal;

        for (let y = startY; y < endY; y += spacing) {
            for (let x = startX; x < endX; x += spacing) {
                // For the current grid point (x, y) on the rotated canvas,
                // find its corresponding coordinate on the original, un-rotated image.
                const origX = x - width / 2;
                const origY = y - height / 2;
                const sampleX = Math.round(origX * cos - origY * sin + width / 2);
                const sampleY = Math.round(origX * sin + origY * cos + height / 2);

                if (sampleX >= 0 && sampleX < width && sampleY >= 0 && sampleY < height) {
                    const pixelIndex = (sampleY * width + sampleX) * 4;
                    const r = imageData[pixelIndex];
                    const g = imageData[pixelIndex + 1];
                    const b = imageData[pixelIndex + 2];
                    
                    const cmyk = rgbToCmyk(r, g, b);
                    const channelValue = cmyk[i]; // Get the value for the current channel (C, M, Y, or K)

                    if (channelValue > 0) {
                        // Calculate dot radius so its area is proportional to the color intensity.
                        const radius = Math.sqrt((channelValue * (spacing * spacing)) / Math.PI) * dotScale;
                        
                        // Draw the dot if it's large enough to be visible
                        if (radius > 0.1) {
                            ctx.beginPath();
                            ctx.arc(x, y, radius, 0, Math.PI * 2, true);
                            ctx.fill();
                        }
                    }
                }
            }
        }
        ctx.restore(); // Restore canvas to its un-rotated state for the next channel
    }

    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 Halftone Effect Applicator allows users to apply a stylistic CMYK halftone effect to their images, mimicking the appearance of comic books or vintage newspaper prints. This tool is useful for graphic designers, artists, and hobbyists looking to create unique visuals that combine traditional printing techniques with digital imagery. Users can customize aspects such as the spacing between halftone dots, the overall size of the dots, and the angle of the black channel to achieve personalized results. This effect is particularly beneficial for projects requiring a retro aesthetic or for enhancing visual storytelling.

Leave a Reply

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