You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, intensityStr = "70", clawMarksStr = "true") {
// Parse parameters
let intensity = parseInt(intensityStr);
if (isNaN(intensity)) intensity = 70;
intensity = Math.max(0, Math.min(100, intensity));
let drawClaws = clawMarksStr === "true";
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(originalImg, 0, 0);
// Apply moonlight/werewolf tint, noise and contrast
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imgData.data;
const contrastFactor = 1 + (intensity / 80); // Up to 2.25x contrast
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// Calculate original luminance
let lum = 0.299 * r + 0.587 * g + 0.114 * b;
// Apply contrast
lum = ((lum / 255 - 0.5) * contrastFactor + 0.5) * 255;
// Add coarse noise for gritty fur/skin texture
let noise = (Math.random() - 0.5) * 50 * (intensity / 100);
lum = lum + noise;
lum = Math.max(0, Math.min(255, lum));
let outR = lum;
let outG = lum;
let outB = lum;
// Night / Moonlight color mapping (Cinematic Feral Vibe)
if (lum < 100) {
// Dark shadows become deep blue-cyan
outR = lum * 0.4;
outG = lum * 0.7;
outB = lum * 0.9;
} else if (lum > 220) {
// Extreme highlights become fiery/glowing yellow (feral eyes/teeth reflection)
let glow = (lum - 220) / 35; // 0 to 1
outR = lum + glow * 55;
outG = lum + glow * 35;
outB = Math.max(0, lum - glow * 100);
} else {
// Midtones get a yellowish-green sickly moonlight cast
let hFac = (lum - 100) / 120;
outR = lum * (1 + 0.05 * hFac);
outG = lum * (1 + 0.1 * hFac);
outB = lum * (1 - 0.15 * hFac);
}
// Blend with original baseed on intensity (higher intensity = more transformation)
const blend = 1 - (intensity / 100);
data[i] = r * blend + Math.min(255, outR) * (1 - blend);
data[i + 1] = g * blend + Math.min(255, outG) * (1 - blend);
data[i + 2] = b * blend + Math.min(255, outB) * (1 - blend);
}
ctx.putImageData(imgData, 0, 0);
// Overlay Fur Texture Strokes
ctx.globalCompositeOperation = 'soft-light';
let area = canvas.width * canvas.height;
// Calculate number of fur strokes based on image size and intensity, cap for performance
let numStrokes = Math.floor(area / 150 * (intensity / 100));
numStrokes = Math.min(numStrokes, 25000);
for (let i = 0; i < numStrokes; i++) {
let x = Math.random() * canvas.width;
let y = Math.random() * canvas.height;
let dx = x - canvas.width / 2;
let dy = y - canvas.height / 2;
// Fur typically flows outward and downward
let angle = Math.PI / 2 + (dx / canvas.width) * 1.5 + (Math.random() - 0.5) * 0.4;
let len = Math.max(5, Math.min(canvas.width, canvas.height) * 0.015) + Math.random() * 20;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.quadraticCurveTo(
x + Math.cos(angle - 0.3) * len / 2,
y + Math.sin(angle - 0.3) * len / 2,
x + Math.cos(angle) * len,
y + Math.sin(angle) * len
);
// Fur color palette: Black, Dirty Brown, Ash Silver
let rand = Math.random();
if(rand < 0.5) ctx.strokeStyle = `rgba(5, 5, 5, ${0.4 * (intensity/100)})`;
else if(rand < 0.8) ctx.strokeStyle = `rgba(60, 40, 20, ${0.5 * (intensity/100)})`;
else ctx.strokeStyle = `rgba(200, 200, 200, ${0.3 * (intensity/100)})`;
ctx.lineWidth = 1 + Math.random() * 2;
ctx.stroke();
}
// Add Vignette for spooky framing
ctx.globalCompositeOperation = 'multiply';
let grad = ctx.createRadialGradient(
canvas.width / 2, canvas.height / 2, Math.min(canvas.width, canvas.height) * 0.25,
canvas.width / 2, canvas.height / 2, Math.max(canvas.width, canvas.height) * 0.85
);
grad.addColorStop(0, 'rgba(255, 255, 255, 1)');
let vignetteOpacity = 0.4 + (intensity / 160); // Max 1.0 opacity at edges
grad.addColorStop(1, `rgba(0, 0, 0, ${vignetteOpacity})`);
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Add Werewolf Claw Scratch Marks
if (drawClaws) {
ctx.globalCompositeOperation = 'source-over';
let numScratches = 1 + Math.floor(Math.random() * 2);
for (let s = 0; s < numScratches; s++) {
let sx = canvas.width * (0.15 + Math.random() * 0.7);
let sy = canvas.height * (0.15 + Math.random() * 0.7);
let sAngle = (Math.random() * Math.PI / 2) + Math.PI / 6;
let sLength = Math.min(canvas.width, canvas.height) * (0.25 + Math.random() * 0.2);
let sOffset = sLength * 0.15; // Width apart
let numClaws = Math.floor(Math.random() * 2) + 3; // 3 to 4 slashes
for (let c = 0; c < numClaws; c++) {
let posX = sx + Math.cos(sAngle + Math.PI/2) * (c - (numClaws-1)/2) * sOffset;
let posY = sy + Math.sin(sAngle + Math.PI/2) * (c - (numClaws-1)/2) * sOffset;
let clawLen = sLength * (0.8 + Math.random() * 0.4);
let startX = posX - Math.cos(sAngle) * clawLen / 2;
let startY = posY - Math.sin(sAngle) * clawLen / 2;
let endX = posX + Math.cos(sAngle) * clawLen / 2;
let endY = posY + Math.sin(sAngle) * clawLen / 2;
// Outer darker scratch (Dried blood/scab)
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.quadraticCurveTo(
posX + Math.cos(sAngle - Math.PI/2) * clawLen * 0.08,
posY + Math.sin(sAngle - Math.PI/2) * clawLen * 0.08,
endX, endY
);
ctx.strokeStyle = `rgba(40, 5, 5, ${0.7 + Math.random() * 0.3})`;
ctx.lineWidth = sLength * 0.06;
ctx.lineCap = 'round';
ctx.stroke();
// Inner bright scratch (Fresh slash reflection)
ctx.beginPath();
ctx.moveTo(
posX - Math.cos(sAngle) * clawLen * 0.42,
posY - Math.sin(sAngle) * clawLen * 0.42
);
ctx.quadraticCurveTo(
posX + Math.cos(sAngle - Math.PI/2) * clawLen * 0.08,
posY + Math.sin(sAngle - Math.PI/2) * clawLen * 0.08,
posX + Math.cos(sAngle) * clawLen * 0.42,
posY + Math.sin(sAngle) * clawLen * 0.42
);
ctx.strokeStyle = `rgba(200, 20, 20, ${0.6 + Math.random() * 0.4})`;
ctx.lineWidth = sLength * 0.015;
ctx.stroke();
}
}
}
return canvas;
}
Apply Changes