You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, amplitude = 20, frequency = 3, verticalModulation = 1.0, flowIntensity = 0.3, tintColorHex = "B8860B", tintOpacity = 0.25) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } as a hint for performance with getImageData/putImageData
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Use naturalWidth/Height for intrinsic image dimensions for robustness
canvas.width = originalImg.naturalWidth || originalImg.width;
canvas.height = originalImg.naturalHeight || originalImg.height;
// If image dimensions are invalid, return an empty canvas
if (canvas.width === 0 || canvas.height === 0) {
// console.warn("Image has zero width or height. Returning empty canvas.");
return canvas;
}
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// This can happen due to cross-origin restrictions if the image is not hosted on the same domain.
// console.error("Could not get image data, possibly due to cross-origin restrictions:", e);
// Fallback: return canvas with the original image drawn, without the effect.
return canvas;
}
const data = imageData.data;
const outputImageData = ctx.createImageData(canvas.width, canvas.height);
const outputData = outputImageData.data;
const width = canvas.width;
const height = canvas.height;
const PI2 = Math.PI * 2;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Calculate effective amplitude, which can increase towards the bottom of the image
const currentEffectiveAmplitude = amplitude * (1 + (y / height) * flowIntensity);
// Calculate the phase for the sine wave.
// The (x / width) term creates vertical "waves" or "rivulets" (displacement varies with x).
// The (y / height) term shifts the phase of these waves as y changes,
// making the rivulets appear to twist or meander downwards.
const phase = (x / width) * frequency * PI2 +
(y / height) * verticalModulation * PI2;
const offsetY = currentEffectiveAmplitude * Math.sin(phase);
// Determine the source Y coordinate to sample from.
// A positive offsetY means pixels are pulled downwards (content from y - offsetY moves to current y).
let srcY = Math.round(y - offsetY);
// Clamp srcY to be within image bounds to avoid reading outside the array
srcY = Math.max(0, Math.min(height - 1, srcY));
const srcX = x; // No horizontal displacement in this particular model
const srcIndex = (srcY * width + srcX) * 4;
const destIndex = (y * width + x) * 4;
// Copy RGBA components from source pixel to destination pixel
outputData[destIndex] = data[srcIndex]; // Red
outputData[destIndex + 1] = data[srcIndex + 1]; // Green
outputData[destIndex + 2] = data[srcIndex + 2]; // Blue
outputData[destIndex + 3] = data[srcIndex + 3]; // Alpha (preserve original alpha)
}
}
// Put the modified image data back onto the canvas
ctx.putImageData(outputImageData, 0, 0);
// Apply the "syrup" color tint
if (tintOpacity > 0 && tintColorHex) {
let sanitizedTintColorHex = tintColorHex;
if (typeof sanitizedTintColorHex === 'string' && sanitizedTintColorHex.startsWith("#")) {
sanitizedTintColorHex = sanitizedTintColorHex.substring(1);
}
if (typeof sanitizedTintColorHex === 'string' &&
sanitizedTintColorHex.length === 6 &&
/^[0-9A-Fa-f]{6}$/.test(sanitizedTintColorHex)) {
const r = parseInt(sanitizedTintColorHex.substring(0, 2), 16);
const g = parseInt(sanitizedTintColorHex.substring(2, 4), 16);
const b = parseInt(sanitizedTintColorHex.substring(4, 6), 16);
// Clamp opacity to be between 0 and 1
const clampedOpacity = Math.max(0, Math.min(1, tintOpacity));
ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
ctx.fillRect(0, 0, width, height);
} else if (tintOpacity > 0) { // Only warn if tint was intended
// console.warn(`Invalid tintColorHex ('${tintColorHex}') provided. It should be a 6-digit hex string (e.g., "B8860B"). Skipping tint.`);
}
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Syrup Pour Filter Effect Tool provides a unique way to enhance your images by applying a syrup-like flow effect. This tool enables users to manipulate the appearance of their images, creating a visually appealing ‘pouring’ effect that resembles syrup cascading down. Parameters such as amplitude, frequency, vertical modulation, and color tint allow for customized adjustments to achieve the desired artistic effect. This tool is ideal for artists, graphic designers, or hobbyists looking to add dynamic elements to their images for creative projects, social media posts, or promotional materials.