You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
patternType = 'lines', // 'lines', 'circles', 'grid'
spacing = '6', // Distance between lines/circles
lineThickness = '2', // Thickness of the lines
angle1 = '0', // Rotation angle of first pattern layer
angle2 = '5', // Difference in rotation/shift for the second layer (Creates Moiré)
color1 = 'rgba(0,0,0,0.8)', // Color of first layer
color2 = 'rgba(0,0,0,0.8)', // Color of second layer
blendMode = 'source-over', // Composite operation, e.g., 'difference', 'multiply', 'overlay'
addDecorations = 'yes', // 'yes' or 'no'
decorationType = 'mixed' // 'stars', 'circles', 'polygons', 'mixed'
) {
// Parse numeric string parameters
const parsedSpacing = Math.max(1, Number(spacing));
const parsedThickness = Math.max(0.1, Number(lineThickness));
const parsedAngle1 = Number(angle1);
const parsedAngle2 = Number(angle2);
// Setup canvas
const canvas = document.createElement('canvas');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 1. Draw base original image
ctx.drawImage(originalImg, 0, 0);
// 2. Add decorative background/foreground shapes if requested
if (String(addDecorations).toLowerCase() === 'yes') {
// Base number of shapes on image size to look proportionate
const numShapes = Math.floor((width * height) / 50000);
const colors = ['#FF4B4B', '#4BFF75', '#4B75FF', '#FF4BF5', '#F5FF4B', '#4BFFFF', '#FFFFFF'];
for (let i = 0; i < Math.max(5, numShapes); i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = Math.random() * 40 + 20;
const color = colors[Math.floor(Math.random() * colors.length)];
ctx.save();
ctx.translate(x, y);
ctx.rotate(Math.random() * Math.PI * 2);
ctx.globalAlpha = 0.5;
ctx.fillStyle = color;
ctx.shadowColor = color;
ctx.shadowBlur = 10;
let type = decorationType.toLowerCase();
if (type === 'mixed') {
const types = ['stars', 'circles', 'polygons'];
type = types[Math.floor(Math.random() * types.length)];
}
ctx.beginPath();
if (type === 'stars') {
for (let j = 0; j < 5; j++) {
ctx.lineTo(Math.cos((18 + j * 72) * Math.PI / 180) * size,
-Math.sin((18 + j * 72) * Math.PI / 180) * size);
ctx.lineTo(Math.cos((54 + j * 72) * Math.PI / 180) * (size / 2),
-Math.sin((54 + j * 72) * Math.PI / 180) * (size / 2));
}
} else if (type === 'circles') {
ctx.arc(0, 0, size / 2, 0, Math.PI * 2);
} else { // polygons (hexagons)
for (let j = 0; j < 6; j++) {
ctx.lineTo((size * 0.8) * Math.cos(j * 2 * Math.PI / 6),
(size * 0.8) * Math.sin(j * 2 * Math.PI / 6));
}
}
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
// 3. Setup Moiré generator bounds
const maxDist = Math.sqrt(width * width + height * height) / 2 + 100; // Extend beyond corners
function drawPattern(mode, color, rotationAngle, dx = 0, dy = 0) {
ctx.save();
ctx.translate(width / 2 + dx, height / 2 + dy);
ctx.rotate(rotationAngle * Math.PI / 180);
const validCompositeTypes = ['source-over', 'multiply', 'screen', 'overlay', 'darken', 'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light', 'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];
ctx.globalCompositeOperation = validCompositeTypes.includes(blendMode) ? blendMode : 'source-over';
ctx.strokeStyle = color;
ctx.lineWidth = parsedThickness;
ctx.beginPath();
if (mode === 'lines') {
for (let i = -maxDist; i <= maxDist; i += parsedSpacing) {
ctx.moveTo(-maxDist, i);
ctx.lineTo(maxDist, i);
}
} else if (mode === 'circles') {
for (let i = parsedSpacing; i <= maxDist; i += parsedSpacing) {
ctx.moveTo(i, 0);
ctx.arc(0, 0, i, 0, Math.PI * 2);
}
} else if (mode === 'grid') {
for (let i = -maxDist; i <= maxDist; i += parsedSpacing) {
// Horizontal
ctx.moveTo(-maxDist, i);
ctx.lineTo(maxDist, i);
// Vertical
ctx.moveTo(i, -maxDist);
ctx.lineTo(i, maxDist);
}
}
ctx.stroke();
ctx.restore();
}
// 4. Draw Layer 1
drawPattern(patternType.toLowerCase(), color1, parsedAngle1);
// 5. Draw Layer 2 with an offset to create the Moiré effect
// Note: Concentric circles need translation (shifting center) rather than just rotation to show moiré
let dx2 = 0;
let dy2 = 0;
let finalAngle2 = parsedAngle1 + parsedAngle2;
if (patternType.toLowerCase() === 'circles') {
// Shift context by angle amount so moiré pattern forms dynamically for circles
dx2 = parsedAngle2;
dy2 = parsedAngle2 / 2;
// Rotation doesn't affect identical concentric circles, so we rely on dx/dy
}
drawPattern(patternType.toLowerCase(), color2, finalAngle2, dx2, dy2);
return canvas;
}
Apply Changes