You can edit the below JavaScript code to customize the image tool.
/**
* Converts an image to a silk print style effect, featuring posterization, optional halftoning, and outlines.
*
* @param {Image} originalImg The original image object to be processed.
* @param {number} numColors The target number of colors for posterization. Fewer colors create a more stylized look.
* @param {number} halftoneSize The size of the halftone pattern grid. Set to 0 or 1 to disable halftoning and use solid colors.
* @param {number} outlineWidth The width of the black outlines around color areas. Set to 0 to disable outlines.
* @param {string} outlineColor The color of the outlines in a CSS format (e.g., 'black', '#000000').
* @returns {HTMLCanvasElement} A canvas element with the silk print effect applied.
*/
async function processImage(originalImg, numColors = 4, halftoneSize = 0, outlineWidth = 1, outlineColor = 'black') {
// 1. Setup Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// Create a temporary canvas to hold the posterized version of the image.
// This will be the base for all subsequent effects.
const posterizedCanvas = document.createElement('canvas');
posterizedCanvas.width = w;
posterizedCanvas.height = h;
const posterizedCtx = posterizedCanvas.getContext('2d', { willReadFrequently: true });
// 2. Posterize Image
posterizedCtx.drawImage(originalImg, 0, 0, w, h);
const imageData = posterizedCtx.getImageData(0, 0, w, h);
const data = imageData.data;
const factor = 255 / (Math.max(2, numColors) - 1);
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.round(data[i] / factor) * factor;
data[i + 1] = Math.round(data[i + 1] / factor) * factor;
data[i + 2] = Math.round(data[i + 2] / factor) * factor;
}
posterizedCtx.putImageData(imageData, 0, 0);
// This canvas will be the source for our final layers, containing either solid or halftoned colors.
let fillSourceCanvas = posterizedCanvas;
// 3. Halftone (Optional)
if (halftoneSize > 1) {
const getLuminance = (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b;
const palette = new Map();
const posterizedData = posterizedCtx.getImageData(0, 0, w, h).data;
// Find unique colors and their luminance to determine the background.
for (let i = 0; i < posterizedData.length; i += 4) {
const r = posterizedData[i], g = posterizedData[i + 1], b = posterizedData[i + 2];
const key = `${r},${g},${b}`;
if (!palette.has(key)) {
palette.set(key, { r, g, b, lum: getLuminance(r, g, b) });
}
}
const sortedPalette = [...palette.values()].sort((a, b) => a.lum - b.lum);
const colorToIndex = new Map(sortedPalette.map((c, i) => [`${c.r},${c.g},${c.b}`, i]));
const halftoneCanvas = document.createElement('canvas');
halftoneCanvas.width = w;
halftoneCanvas.height = h;
const halftoneCtx = halftoneCanvas.getContext('2d');
const halftoneImageData = halftoneCtx.createImageData(w, h);
const halftoneData = halftoneImageData.data;
const bgColor = sortedPalette[0] || { r: 255, g: 255, b: 255 }; // Default to white if no colors
for (let i = 0; i < posterizedData.length; i += 4) {
const r = posterizedData[i], g = posterizedData[i+1], b = posterizedData[i+2];
const x = (i / 4) % w;
const y = Math.floor((i / 4) / w);
const key = `${r},${g},${b}`;
const colorIndex = colorToIndex.get(key);
let draw = false;
if (colorIndex > 0) { // Don't apply pattern to the lightest color (background)
// Use a different screen pattern for each color index to simulate different print screens
switch(colorIndex % 4) {
case 0: draw = (x + y) % halftoneSize === 0; break;
case 1: draw = (x % halftoneSize === 0 && y % halftoneSize === 0); break;
case 2: draw = y % halftoneSize === 0; break;
case 3: draw = x % halftoneSize === 0; break;
}
}
if (draw) {
halftoneData[i] = r;
halftoneData[i+1] = g;
halftoneData[i+2] = b;
halftoneData[i+3] = 255;
} else {
halftoneData[i] = bgColor.r;
halftoneData[i+1] = bgColor.g;
halftoneData[i+2] = bgColor.b;
halftoneData[i+3] = 255;
}
}
halftoneCtx.putImageData(halftoneImageData, 0, 0);
fillSourceCanvas = halftoneCanvas;
}
// 4. Draw Layers
ctx.clearRect(0, 0, w, h);
// A. Draw Outline Layer by creating a colored silhouette and stamping it
if (outlineWidth > 0 && outlineColor && outlineColor !== 'transparent') {
const outlineCanvas = document.createElement('canvas');
outlineCanvas.width = w;
outlineCanvas.height = h;
const outlineCtx = outlineCanvas.getContext('2d');
// Create a silhouette of the image in the specified outline color
outlineCtx.drawImage(fillSourceCanvas, 0, 0);
outlineCtx.globalCompositeOperation = 'source-in';
outlineCtx.fillStyle = outlineColor;
outlineCtx.fillRect(0, 0, w, h);
// Stamp the silhouette around the center on the main canvas to create the outline effect
const directions = [
[-1, -1], [0, -1], [1, -1],
[-1, 0], [1, 0],
[-1, 1], [0, 1], [1, 1]
];
directions.forEach(([dx, dy]) => {
ctx.drawImage(outlineCanvas, dx * outlineWidth, dy * outlineWidth);
});
}
// B. Draw the main color/halftone Fill Layer on top of the outlines
ctx.drawImage(fillSourceCanvas, 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!
The Image To Silk Print Converter allows users to transform standard images into a stylized silk print effect. By applying posterization techniques, users can reduce the number of colors in the image, resulting in a more artistic appearance. Additionally, the tool includes options for halftoning, where users can implement a dotted print pattern, and outline creation, adding defined borders around color areas. This tool is ideal for graphic designers, artists, and anyone looking to create unique print designs or artistic renderings from their photos.