You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, threshold = 220, maxSparkles = 50, sizePercentage = 5, colorRGB = "255, 255, 255") {
// Parse inputs
const thresholdNum = Number(threshold) || 220;
const countNum = Number(maxSparkles) || 50;
const sizeRatio = (Number(sizePercentage) || 5) / 100;
// Parse color string (expecting a format like "255, 255, 255")
const colorStr = String(colorRGB).replace(/[^\d,]/g, '').trim() || "255,255,255";
let [cr, cg, cb] = colorStr.split(',').map(Number);
cr = isNaN(cr) ? 255 : Math.max(0, Math.min(255, cr));
cg = isNaN(cg) ? 255 : Math.max(0, Math.min(255, cg));
cb = isNaN(cb) ? 255 : Math.max(0, Math.min(255, cb));
// Setup canvas
const canvas = document.createElement('canvas');
const w = parseInt(originalImg.width);
const h = parseInt(originalImg.height);
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, w, h);
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
let brightSpots = [];
// Calculate a dynamic step size to skip pixels to maintain performance on huge images
const step = Math.max(1, Math.ceil(Math.sqrt((w * h) / 500000)));
// Find all bright spots exceeding the threshold
for (let y = 0; y < h; y += step) {
for (let x = 0; x < w; x += step) {
const i = (y * w + x) * 4;
// RGB Luminance calculation
const luma = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
if (luma >= thresholdNum) {
brightSpots.push({ x, y, luma });
}
}
}
// If no bright spots are found (dark image), sprinkle some randomly or on the brightest found
if (brightSpots.length === 0) {
for (let i = 0; i < countNum; i++) {
brightSpots.push({
x: Math.random() * w,
y: Math.random() * h,
luma: 255
});
}
} else {
// Shuffle the bright spots to avoid clustering (Fisher-Yates shuffle)
for (let i = brightSpots.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[brightSpots[i], brightSpots[j]] = [brightSpots[j], brightSpots[i]];
}
}
// Cap the array to the requested maximum sparkles count
const spotsToDraw = brightSpots.slice(0, countNum);
// Set operation blending mode to screen for a natural glowing/bloom look
ctx.globalCompositeOperation = 'screen';
// Base sparkle size relative to the canvas dimensions
const baseSize = Math.max(w, h) * sizeRatio;
// Sparkle rendering logic
for (const spot of spotsToDraw) {
// Vary each sparkle size randomly by multiplying base size between 0.5x and 1.5x
const s = baseSize * (0.5 + Math.random() * 1.0);
ctx.save();
ctx.translate(spot.x, spot.y);
ctx.rotate(Math.random() * Math.PI / 4); // Add random slight twist
ctx.fillStyle = `rgb(${cr},${cg},${cb})`;
// Main geometric sparkle cross
drawStar(ctx, s, 0.03);
// Secondary cross (smaller, rotated by 45 degrees)
ctx.save();
ctx.rotate(Math.PI / 4);
drawStar(ctx, s * 0.6, 0.08);
ctx.restore();
// Inner intense white core
ctx.beginPath();
ctx.arc(0, 0, s * 0.08, 0, Math.PI * 2);
ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
ctx.fill();
// Soft outer radial glow
const glowGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, s * 0.5);
glowGrad.addColorStop(0, `rgba(${cr},${cg},${cb}, 0.8)`);
glowGrad.addColorStop(1, `rgba(${cr},${cg},${cb}, 0)`);
ctx.beginPath();
ctx.arc(0, 0, s * 0.5, 0, Math.PI * 2);
ctx.fillStyle = glowGrad;
ctx.fill();
ctx.restore();
}
// Reset global composite operation back to default
ctx.globalCompositeOperation = 'source-over';
return canvas;
// Helper function to draw a beautiful 4-point star lens flare using quadratic curves
function drawStar(context, radius, thinness) {
const p = radius * thinness;
context.beginPath();
context.moveTo(0, -radius);
context.quadraticCurveTo(p, -p, radius, 0);
context.quadraticCurveTo(p, p, 0, radius);
context.quadraticCurveTo(-p, p, -radius, 0);
context.quadraticCurveTo(-p, -p, 0, -radius);
context.closePath();
context.fill();
}
}
Apply Changes