You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, glowColor = '#00ffcc', glowIntensity = 10, backgroundDarkness = 90, edgeSmoothing = 2, edgeSensitivity = 20) {
// Parameter normalization
glowIntensity = Math.abs(Number(glowIntensity));
if (isNaN(glowIntensity)) glowIntensity = 10;
backgroundDarkness = Math.min(100, Math.max(0, Number(backgroundDarkness)));
if (isNaN(backgroundDarkness)) backgroundDarkness = 90;
edgeSmoothing = Math.abs(Number(edgeSmoothing));
if (isNaN(edgeSmoothing)) edgeSmoothing = 2;
edgeSensitivity = Math.abs(Number(edgeSensitivity));
if (isNaN(edgeSensitivity)) edgeSensitivity = 20;
const width = originalImg.width || originalImg.naturalWidth;
const height = originalImg.height || originalImg.naturalHeight;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Parse the requested neon glow color into RGB using canvas fillStyle
ctx.fillStyle = glowColor;
ctx.fillRect(0, 0, 1, 1);
const colorData = ctx.getImageData(0, 0, 1, 1).data;
const rC = colorData[0], gC = colorData[1], bC = colorData[2];
ctx.clearRect(0, 0, width, height);
// Render the original image temporarily with blur to suppress noise/texture and extract structural "skeleton" lines
ctx.filter = `blur(${edgeSmoothing}px)`;
ctx.drawImage(originalImg, 0, 0, width, height);
ctx.filter = 'none';
// Retrieve pixel data for Sobel Edge Detection processing
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// Create a grayscale channel
const gray = new Float32Array(width * height);
for (let i = 0; i < data.length; i += 4) {
gray[i / 4] = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
}
const edgeData = new Uint8ClampedArray(width * height * 4);
// Sobel operator math
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
const i = y * width + x;
// Map surrounding pixels
const i00 = i - width - 1, i01 = i - width, i02 = i - width + 1;
const i10 = i - 1, i12 = i + 1;
const i20 = i + width - 1, i21 = i + width, i22 = i + width + 1;
const gx = -gray[i00] + gray[i02]
-2 * gray[i10] + 2 * gray[i12]
-gray[i20] + gray[i22];
const gy = -gray[i00] - 2 * gray[i01] - gray[i02]
+gray[i20] + 2 * gray[i21] + gray[i22];
const mag = Math.sqrt(gx * gx + gy * gy);
if (mag > edgeSensitivity) {
// Apply color and create a gradual anti-aliased edge based on gradient intensity
const alpha = Math.min(255, (mag - edgeSensitivity) * 1.5);
const outIdx = i * 4;
edgeData[outIdx] = rC;
edgeData[outIdx + 1] = gC;
edgeData[outIdx + 2] = bC;
edgeData[outIdx + 3] = alpha;
}
}
}
// Convert edges back into canvas standard element
const edgeCanvas = document.createElement('canvas');
edgeCanvas.width = width;
edgeCanvas.height = height;
const edgeCtx = edgeCanvas.getContext('2d');
edgeCtx.putImageData(new ImageData(edgeData, width, height), 0, 0);
const finalCanvas = document.createElement('canvas');
finalCanvas.width = width;
finalCanvas.height = height;
const fCtx = finalCanvas.getContext('2d');
// 1. Render base context (keep original image crisp but dark for the dramatic neon filter background context)
fCtx.drawImage(originalImg, 0, 0, width, height);
fCtx.fillStyle = `rgba(0, 0, 0, ${backgroundDarkness / 100})`;
fCtx.fillRect(0, 0, width, height);
// 2. Composite Glowing Overlays
// Using screen blend mode creates an authentic illuminated/neon effect
fCtx.globalCompositeOperation = 'screen';
if (glowIntensity > 0) {
// Broad scattered ambient outer-glow
fCtx.filter = `blur(${glowIntensity * 1.5}px)`;
fCtx.globalAlpha = 0.5;
fCtx.drawImage(edgeCanvas, 0, 0);
// Core dense inner-glow
fCtx.filter = `blur(${glowIntensity * 0.5}px)`;
fCtx.globalAlpha = 0.8;
fCtx.drawImage(edgeCanvas, 0, 0);
}
// Standard structural line rendered crisply on top
fCtx.filter = 'none';
fCtx.globalAlpha = 1.0;
fCtx.drawImage(edgeCanvas, 0, 0);
return finalCanvas;
}
Apply Changes