You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, blurAmount = 10, circleScale = 1, brightnessThreshold = 220, highlightDensity = 0.05) {
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (!imgWidth || !imgHeight) {
console.error("Image has no dimensions or is not loaded/valid.");
const errCanvas = document.createElement('canvas');
errCanvas.width = 150;
errCanvas.height = 30;
const errCtx = errCanvas.getContext('2d');
if (errCtx) {
// Basic error message on a small canvas
errCtx.fillStyle = 'lightgray';
errCtx.fillRect(0, 0, errCanvas.width, errCanvas.height);
errCtx.fillStyle = 'red';
errCtx.font = '12px Arial';
errCtx.textAlign = 'center';
errCtx.fillText("Invalid Image Object", errCanvas.width / 2, errCanvas.height / 2 + 4);
}
return errCanvas;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = imgWidth;
canvas.height = imgHeight;
// Step 1: Draw blurred background
ctx.filter = `blur(${Math.max(0, blurAmount)}px)`;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
ctx.filter = 'none'; // Reset filter for drawing bokeh circles
// Step 2: Identify bright spots from the original image (without blur)
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCtx.drawImage(originalImg, 0, 0, tempCanvas.width, tempCanvas.height);
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
} catch (e) {
console.error("Could not getImageData (possibly CORS issue with the image source):", e);
// Fallback: return the blurred image without bokeh circles if getImageData fails.
return canvas;
}
const data = imageData.data;
const brightSpots = [];
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const a = data[i + 3];
const brightness = 0.299 * r + 0.587 * g + 0.114 * b; // Luminance
if (brightness > brightnessThreshold && a > 128) { // Check alpha to ignore transparent areas
if (Math.random() < highlightDensity) { // Apply density probability
const x = (i / 4) % tempCanvas.width;
const y = Math.floor((i / 4) / tempCanvas.width);
brightSpots.push({ x, y, r, g, b });
}
}
}
// Step 3: Draw bokeh circles on the main canvas
const baseBokehRadius = Math.max(8, Math.min(canvas.width, canvas.height) * 0.04);
// Use 'lighter' composite operation for additive blending of bokeh lights
ctx.globalCompositeOperation = 'lighter';
for (const spot of brightSpots) {
const radius = (Math.random() * 0.6 + 0.4) * baseBokehRadius * Math.max(0.1, circleScale);
if (radius < 1) continue; // Skip very small circles
// Enhance original spot color to make bokeh lights brighter/more vivid
const bokehR = Math.min(255, Math.floor(spot.r * 1.05 + 25));
const bokehG = Math.min(255, Math.floor(spot.g * 1.05 + 25));
const bokehB = Math.min(255, Math.floor(spot.b * 1.05 + 25));
// Create a radial gradient for a soft-edged circle with a hot-spot
// Inner radius (hotspot) is small relative to the full radius
const gradient = ctx.createRadialGradient(spot.x, spot.y, radius * 0.05, spot.x, spot.y, radius);
gradient.addColorStop(0, `rgba(${bokehR},${bokehG},${bokehB},0.85)`); // Bright, slightly transparent center
gradient.addColorStop(0.25, `rgba(${bokehR},${bokehG},${bokehB},0.6)`); // Mid-core, smoothly transitions
gradient.addColorStop(1, `rgba(${bokehR},${bokehG},${bokehB},0)`); // Edge, fully transparent
ctx.beginPath();
ctx.arc(spot.x, spot.y, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
}
// Reset composite operation to default
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes