You can edit the below JavaScript code to customize the image tool.
/**
* Converts an image to a "silkprint" style by reducing the number of colors (posterization)
* and optionally adding outlines around the resulting flat color areas.
*
* @param {Image} originalImg The original image object to be processed. Assumed to be fully loaded.
* @param {number | string} levels The number of color levels per channel for posterization. Fewer levels create a more abstract, blocky look. Must be 2 or greater. Defaults to 4.
* @param {string} addOutlines A string 'true' or 'false' to determine whether to add outlines around color areas. Defaults to 'true'.
* @param {string} outlineColor A CSS color string for the outlines (e.g., '#000000', 'black', 'rgb(0,0,0)'). Defaults to '#000000'.
* @returns {HTMLCanvasElement} A canvas element displaying the silkprint version of the image.
*/
function processImage(originalImg, levels = 4, addOutlines = 'true', outlineColor = '#000000') {
// --- 1. Parameter Parsing and Canvas Setup ---
const numLevels = Math.max(2, Number(levels));
const shouldAddOutlines = String(addOutlines).toLowerCase() === 'true';
const canvas = document.createElement('canvas');
// Use { willReadFrequently: true } for performance optimization when using getImageData frequently.
const ctx = canvas.getContext('2d', {
willReadFrequently: true
});
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
// --- 2. Posterization Step ---
// Draw the original image to get its pixel data.
ctx.drawImage(originalImg, 0, 0, width, height);
const posterizedImageData = ctx.getImageData(0, 0, width, height);
const posterizedData = posterizedImageData.data;
// This factor determines the size of the "bins" for each color channel.
const factor = 255 / (numLevels - 1);
// Iterate over each pixel and quantize its R, G, and B values.
for (let i = 0; i < posterizedData.length; i += 4) {
posterizedData[i] = Math.round(Math.round(posterizedData[i] / factor) * factor);
posterizedData[i + 1] = Math.round(Math.round(posterizedData[i + 1] / factor) * factor);
posterizedData[i + 2] = Math.round(Math.round(posterizedData[i + 2] / factor) * factor);
}
// If no outlines are needed, we can stop here and return the posterized image.
if (!shouldAddOutlines) {
ctx.putImageData(posterizedImageData, 0, 0);
return canvas;
}
// --- 3. Edge Detection and Outline Drawing ---
// We will create the final image in a new pixel buffer.
const finalImageData = ctx.createImageData(width, height);
const finalData = finalImageData.data;
// A helper function to get [R, G, B] values from any valid CSS color string.
const getRgbFromColor = (colorStr) => {
const tempCtx = document.createElement('canvas').getContext('2d');
tempCtx.fillStyle = colorStr;
tempCtx.fillRect(0, 0, 1, 1);
return tempCtx.getImageData(0, 0, 1, 1).data.slice(0, 3);
};
const [outlineR, outlineG, outlineB] = getRgbFromColor(outlineColor);
// Iterate through the posterized data to find edges.
// An edge is a pixel whose color differs from any of its cardinal neighbors.
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = (y * width + x) * 4;
const r = posterizedData[i];
const g = posterizedData[i + 1];
const b = posterizedData[i + 2];
let isEdge = false;
// Check neighbors (top, bottom, left, right)
const neighbors = [
[x, y - 1], // Top
[x, y + 1], // Bottom
[x - 1, y], // Left
[x + 1, y] // Right
];
for (const [nx, ny] of neighbors) {
// Check if the neighbor is within the image bounds
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
const ni = (ny * width + nx) * 4;
if (r !== posterizedData[ni] || g !== posterizedData[ni + 1] || b !== posterizedData[ni + 2]) {
isEdge = true;
break; // Found a different neighbor, no need to check others
}
}
}
// Set the pixel in the final image data buffer
if (isEdge) {
finalData[i] = outlineR;
finalData[i + 1] = outlineG;
finalData[i + 2] = outlineB;
finalData[i + 3] = 255; // Full alpha for the outline
} else {
finalData[i] = r;
finalData[i + 1] = g;
finalData[i + 2] = b;
finalData[i + 3] = posterizedData[i + 3]; // Preserve original alpha
}
}
}
// --- 4. Final Output ---
// Draw the final computed pixel data onto the canvas.
ctx.putImageData(finalImageData, 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 Silkprint Converter transforms images into a stylized ‘silkprint’ effect by reducing the number of colors, creating a simplified and abstract appearance. Users can customize the level of color reduction and choose to add outlines around color areas, enhancing the graphic look. This tool is ideal for artists, designers, and hobbyists looking to create unique visual art, logos, or print designs that require a distinctive, blocky aesthetic.