You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts an image into a glossy metallic embossed version.
*
* @param {Image} originalImg The original javascript Image object.
* @param {number} embossDepth The depth of the emboss effect. Default is 4.
* @param {number} lightAngle The angle of the light source in degrees (0-360). Default is 135 (top-left).
* @param {number} metallicShininess The intensity of the metallic shine (0-100). Default is 50.
* @param {string} tintColor The color to tint the final metallic image. Default is a steel-like blue-gray.
* @returns {HTMLCanvasElement} A canvas element with the rendered image.
*/
function processImage(originalImg, embossDepth = 4, lightAngle = 135, metallicShininess = 50, tintColor = 'rgba(128, 128, 150, 0.5)') {
// 1. Canvas Setup
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize repeated getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
// 2. Convert to Grayscale
// Using the built-in filter is faster than manual pixel manipulation for this step.
ctx.filter = 'grayscale(100%)';
ctx.drawImage(originalImg, 0, 0, width, height);
const grayImageData = ctx.getImageData(0, 0, width, height);
ctx.filter = 'none'; // Reset filter for subsequent operations
// 3. Apply Emboss Filter
const embossImageData = ctx.createImageData(width, height);
const grayData = grayImageData.data;
const embossData = embossImageData.data;
const angleRad = lightAngle * (Math.PI / 180);
const offsetX = Math.cos(angleRad) * embossDepth;
const offsetY = Math.sin(angleRad) * embossDepth;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = (y * width + x) * 4;
const currentGray = grayData[i];
// Get the coordinates of the pixel to compare with, based on light angle
const sx = Math.round(x - offsetX);
const sy = Math.round(y - offsetY);
// Use a default mid-gray for pixels that are off-canvas
let offsetGray = 128;
if (sx >= 0 && sx < width && sy >= 0 && sy < height) {
const si = (sy * width + sx) * 4;
offsetGray = grayData[si];
}
// The difference between the pixels determines the highlight or shadow
const diff = currentGray - offsetGray;
// Center the result around 128 (mid-gray)
const newGray = 128 + diff;
embossData[i] = newGray;
embossData[i + 1] = newGray;
embossData[i + 2] = newGray;
embossData[i + 3] = 255; // Full alpha
}
}
// 4. Apply Metallic Contrast (Shininess)
// Map shininess [0, 100] to a contrast factor, e.g., [1.0, 3.0]
const contrastFactor = (metallicShininess / 50.0) + 1.0;
for (let i = 0; i < embossData.length; i += 4) {
let val = embossData[i];
// Apply contrast formula: push values away from the midpoint (128)
val = (val - 128) * contrastFactor + 128;
// Clamp the value to the valid 0-255 range
val = Math.max(0, Math.min(255, val));
embossData[i] = val;
embossData[i + 1] = val;
embossData[i + 2] = val;
}
// 5. Put the Embossed & Contrasted Data on the Canvas
ctx.putImageData(embossImageData, 0, 0);
// 6. Apply Color Tint
// The 'color' composite mode takes the hue and saturation from the new layer
// and the luminosity from the existing layer.
ctx.globalCompositeOperation = 'color';
ctx.fillStyle = tintColor;
ctx.fillRect(0, 0, width, height);
// 7. Add Glossy Overlay
// 'soft-light' creates a gentle, diffused lighting effect.
ctx.globalCompositeOperation = 'soft-light';
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.7)'); // Highlight at the top
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)'); // Fades in the middle
gradient.addColorStop(1, 'rgba(0, 0, 0, 0.3)'); // Shadow at the bottom
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Reset composite operation to default for future drawing
ctx.globalCompositeOperation = 'source-over';
// 8. Return the final canvas
return canvas;
}
Apply Changes