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!
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.