You can edit the below JavaScript code to customize the image tool.
/**
* Converts an image into a marker drawing style.
*
* @param {Image} originalImg The original image object to be processed.
* @param {number} markerSize The base size (thickness) of the marker strokes. Default is 8.
* @param {number} density A value between 0 and 1 that controls the number of strokes. Higher means more strokes and detail. Default is 0.5.
* @param {string} colors A comma-separated string of hex color codes to use for the marker palette. Default is a standard 8-color set.
* @param {number} angle The general angle of the marker strokes in degrees. Use -1 for random angles for each stroke. Default is 45.
* @returns {HTMLCanvasElement} A new canvas element with the marker-style drawing.
*/
async function processImage(originalImg, markerSize = 8, density = 0.5, colors = '#000000,#FF0000,#0000FF,#008000,#FFFF00,#FFA500,#800080,#A52A2A', angle = 45) {
// 1. Setup Canvases
const width = originalImg.width;
const height = originalImg.height;
// Hidden canvas to get pixel data from the original image
const hiddenCanvas = document.createElement('canvas');
hiddenCanvas.width = width;
hiddenCanvas.height = height;
const hiddenCtx = hiddenCanvas.getContext('2d');
hiddenCtx.drawImage(originalImg, 0, 0);
const imageData = hiddenCtx.getImageData(0, 0, width, height).data;
// Output canvas for drawing the result
const outputCanvas = document.createElement('canvas');
outputCanvas.width = width;
outputCanvas.height = height;
const ctx = outputCanvas.getContext('2d');
// Fill the background with white
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, width, height);
// 2. Prepare Color Palette
const hexToRgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
};
const palette = colors.split(',')
.map(hex => hexToRgb(hex.trim()))
.filter(c => c !== null);
if (palette.length === 0) {
// Add a default black color if the palette is empty or parsing failed
palette.push({ r: 0, g: 0, b: 0 });
}
// 3. Helper function to find the closest color in the palette
const findClosestColor = (r, g, b, colorPalette) => {
let closestColor = colorPalette[0];
let minDistanceSq = Infinity;
for (const color of colorPalette) {
const distanceSq = Math.pow(r - color.r, 2) + Math.pow(g - color.g, 2) + Math.pow(b - color.b, 2);
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
closestColor = color;
}
}
return `rgb(${closestColor.r},${closestColor.g},${closestColor.b})`;
};
// 4. Drawing Logic
ctx.lineCap = 'round';
ctx.globalAlpha = 0.85; // Simulate marker ink transparency and layering
const step = Math.max(1, Math.floor(markerSize * 0.7));
// Create a shuffled list of points to draw in a random order, making it look more natural
const points = [];
for (let y = 0; y < height; y += step) {
for (let x = 0; x < width; x += step) {
points.push({ x, y });
}
}
// Fisher-Yates shuffle for random order
for (let i = points.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[points[i], points[j]] = [points[j], points[i]];
}
for (const point of points) {
// Use density to probabilistically skip some strokes
if (Math.random() > density) {
continue;
}
const { x, y } = point;
const index = (y * width + x) * 4;
const r = imageData[index];
const g = imageData[index + 1];
const b = imageData[index + 2];
const a = imageData[index + 3];
// Skip transparent or very light areas
if (a < 128 || (r > 245 && g > 245 && b > 245)) {
continue;
}
// Find the matching marker color
const closestColor = findClosestColor(r, g, b, palette);
ctx.strokeStyle = closestColor;
// Add random variations to each stroke for a more hand-drawn feel
const currentMarkerSize = markerSize * (0.8 + Math.random() * 0.4);
const strokeLength = currentMarkerSize * 1.5;
ctx.lineWidth = currentMarkerSize;
const baseAngle = (angle === -1) ?
Math.random() * Math.PI * 2 :
angle * Math.PI / 180;
const finalAngle = baseAngle + (Math.random() - 0.5) * (Math.PI / 6); // Add slight angle jitter
const dx = Math.cos(finalAngle) * strokeLength / 2;
const dy = Math.sin(finalAngle) * strokeLength / 2;
ctx.beginPath();
ctx.moveTo(x - dx, y - dy);
ctx.lineTo(x + dx, y + dy);
ctx.stroke();
}
return outputCanvas;
}
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 Marker Converter transforms traditional images into a unique marker drawing style, simulating the appearance of marker strokes on a canvas. Users can customize the thickness and density of the marker strokes, choose from a variety of color palettes, and alter the orientation of the strokes. This tool is ideal for artists and designers looking to create stylized artwork from photographs or digital images, enhancing graphic design projects, educational materials, or personal illustrations.