You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, reflectionHeightFactor = 0.5, opacityStart = 0.5, opacityEnd = 0.0, gap = 0) {
// Ensure parameters are numbers and clamp them to valid ranges
reflectionHeightFactor = Math.max(0, Math.min(1, Number(reflectionHeightFactor)));
opacityStart = Math.max(0, Math.min(1, Number(opacityStart)));
opacityEnd = Math.max(0, Math.min(1, Number(opacityEnd)));
gap = Math.max(0, Number(gap));
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
// If image has no dimensions, return a tiny placeholder canvas
if (imgWidth === 0 || imgHeight === 0) {
canvas.width = 1;
canvas.height = 1;
// console.warn("Image Reflection: Original image has zero width or height.");
return canvas;
}
const reflectionVisibleHeight = imgHeight * reflectionHeightFactor;
canvas.width = imgWidth;
// Canvas height accommodates original image, gap, and the visible part of the reflection
canvas.height = imgHeight + gap + reflectionVisibleHeight;
// 1. Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// If reflection height is too small (e.g., less than 1 pixel),
// no need to draw the reflection. Return canvas with only original image.
if (reflectionVisibleHeight < 1) {
return canvas;
}
// 2. Draw the reflected image.
// This involves flipping the image vertically and positioning it below the original.
ctx.save(); // Save current context state
// Apply a vertical flip. The y-axis will now point upwards from the canvas origin (0,0).
ctx.scale(1, -1);
// Calculate the destination Y coordinate for drawing the image in the *scaled* (flipped) system.
// The reflection's top edge in *canvas coordinates* is `imgHeight + gap`.
// The reflection's bottom edge in *canvas coordinates* is `imgHeight + gap + reflectionVisibleHeight`.
// In the *scaled* coordinate system (where Y_canvas_coord = -Y_scaled_coord for origin at 0,0):
// - Y for top of reflection area (canvas Y: imgHeight + gap) becomes scaled Y: -(imgHeight + gap)
// - Y for bottom of reflection area (canvas Y: imgHeight + gap + reflectionVisibleHeight) becomes scaled Y: -(imgHeight + gap + reflectionVisibleHeight)
//
// `drawImage` in a Flipped-Y system (Y points upwards):
// - `dy` is the Y coordinate of the "bottom-left" corner of the destination rectangle in this Flipped-Y system.
// - `dHeight` extends "upwards" from `dy` in this Flipped-Y system.
// So, `dy` should be `-(imgHeight + gap + reflectionVisibleHeight)`.
const destY_in_flipped_system = -(imgHeight + gap + reflectionVisibleHeight);
ctx.drawImage(
originalImg, // The source image
0, // Source X (sx)
0, // Source Y (sy)
imgWidth, // Source width (sWidth)
imgHeight, // Source height (sHeight) - use the full original image content for reflection
0, // Destination X (dx) in the flipped coordinate system
destY_in_flipped_system, // Destination Y (dy) in the flipped coordinate system
imgWidth, // Destination width (dWidth)
reflectionVisibleHeight // Destination height (dHeight) - the source image is scaled to this height
);
// This mapping ensures that:
// - Bottom of original image (sy=imgHeight) is at the top of the reflection (canvasY = imgHeight + gap).
// - Top of original image (sy=0) is at the bottom of the reflection (canvasY = imgHeight + gap + reflectionVisibleHeight).
ctx.restore(); // Restore context state (removes the flip)
// 3. Apply a fading gradient to the reflection area.
// This uses `globalCompositeOperation = 'destination-in'` to make the reflection fade out.
// The gradient defines the opacity mask for the already drawn reflection.
ctx.save(); // Save current context state
const gradientStartY = imgHeight + gap; // Y-coordinate for the top of the reflection area
const gradientEndY = imgHeight + gap + reflectionVisibleHeight; // Y-coordinate for the bottom
// Create a linear gradient that transitions vertically across the reflection area.
const alphaGradient = ctx.createLinearGradient(
0, gradientStartY, // x0, y0 (start point of gradient - top of reflection)
0, gradientEndY // x1, y1 (end point of gradient - bottom of reflection)
);
// Add color stops to the gradient. These define the opacity from top to bottom.
// Using white with varying alpha for the 'destination-in' operation provides a clean alpha mask.
alphaGradient.addColorStop(0, `rgba(255, 255, 255, ${opacityStart})`); // Opacity at the top
alphaGradient.addColorStop(1, `rgba(255, 255, 255, ${opacityEnd})`); // Opacity at the bottom
ctx.fillStyle = alphaGradient; // Set the fill style to our gradient
// 'destination-in': The existing canvas content (the reflection) is kept where it overlaps
// with the new shape (the gradient-filled rectangle). The new shape's alpha channel
// determines the opacity of the kept content.
ctx.globalCompositeOperation = 'destination-in';
// Fill a rectangle over the reflection area. This applies the gradient mask to the reflection.
ctx.fillRect(
0, // x-coordinate of the rectangle
gradientStartY, // y-coordinate of the rectangle
imgWidth, // width of the rectangle
reflectionVisibleHeight // height of the rectangle
);
ctx.restore(); // Restore context state (globalCompositeOperation, fillStyle)
return canvas; // Return the canvas with the original image and its reflection
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Reflection Effect Adder is a tool that allows users to create a reflection effect for their images. By using this tool, users can adjust the height of the reflection, control the opacity gradient from the top to the bottom of the reflection, and specify a gap between the original image and its reflection. This tool can be particularly useful for enhancing images for presentations, marketing materials, social media posts, and digital artwork, by adding a professional and visually appealing touch to photographs and graphics.