Please bookmark this page to avoid losing your image tool!

Image Clock Gear Filter Effect

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg,
    gearColor = "rgba(100, 100, 100, 0.7)", // string: color of the gears
    numGears = 12, // number: how many gears to draw
    avgGearRadiusFactor = 0.08, // number: average gear radius as a factor of min(image_width, image_height)
    gearRadiusVariationFactor = 0.04, // number: variation in gear radius factor
    minTeeth = 5, // number: minimum teeth on a gear
    maxTeeth = 12, // number: maximum teeth on a gear
    toothDepthFactor = 0.3, // number: depth of teeth as a factor of gear's outer radius
    holeSizeFactor = 0.3 // number: size of central hole as a factor of gear's outer radius
) {

    // --- Parameter Parsing and Validation ---
    const MIN_GEAR_TEETH = 3; // Absolute minimum teeth for a recognizable gear shape
    const DEFAULT_FALLBACK_NUM_GEARS = 12;
    const DEFAULT_FALLBACK_AVG_GEAR_RADIUS_FACTOR = 0.08;
    const DEFAULT_FALLBACK_GEAR_RADIUS_VARIATION_FACTOR = 0.04;
    const DEFAULT_FALLBACK_MIN_TEETH = 5;
    const DEFAULT_FALLBACK_MAX_TEETH = 12;
    const DEFAULT_FALLBACK_TOOTH_DEPTH_FACTOR = 0.3;
    const DEFAULT_FALLBACK_HOLE_SIZE_FACTOR = 0.3;

    const finalGearColor = String(gearColor);

    let pNumGears = Number(numGears);
    pNumGears = isNaN(pNumGears) ? DEFAULT_FALLBACK_NUM_GEARS : Math.max(0, pNumGears);

    let pAvgGearRadiusFactor = Number(avgGearRadiusFactor);
    pAvgGearRadiusFactor = isNaN(pAvgGearRadiusFactor) ? DEFAULT_FALLBACK_AVG_GEAR_RADIUS_FACTOR : Math.max(0.01, pAvgGearRadiusFactor);

    let pGearRadiusVariationFactor = Number(gearRadiusVariationFactor);
    pGearRadiusVariationFactor = isNaN(pGearRadiusVariationFactor) ? DEFAULT_FALLBACK_GEAR_RADIUS_VARIATION_FACTOR : Math.max(0, pGearRadiusVariationFactor);

    let pMinTeeth = Number(minTeeth);
    pMinTeeth = isNaN(pMinTeeth) ? DEFAULT_FALLBACK_MIN_TEETH : Math.max(MIN_GEAR_TEETH, pMinTeeth);

    let pMaxTeeth = Number(maxTeeth);
    pMaxTeeth = isNaN(pMaxTeeth) ? (pMinTeeth + 7) : Math.max(pMinTeeth, pMaxTeeth); // Default max related to min if NaN
    if (pMaxTeeth < pMinTeeth) pMaxTeeth = pMinTeeth; // Ensure max is not less than min

    let pToothDepthFactor = Number(toothDepthFactor);
    pToothDepthFactor = isNaN(pToothDepthFactor) ? DEFAULT_FALLBACK_TOOTH_DEPTH_FACTOR : Math.min(0.9, Math.max(0.05, pToothDepthFactor)); // Clamp to [0.05, 0.9]

    let pHoleSizeFactor = Number(holeSizeFactor);
    pHoleSizeFactor = isNaN(pHoleSizeFactor) ? DEFAULT_FALLBACK_HOLE_SIZE_FACTOR : Math.min(0.95, Math.max(0, pHoleSizeFactor)); // Clamp to [0, 0.95]

    // --- Canvas Setup ---
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    if (canvas.width === 0 || canvas.height === 0) {
        console.warn("Image Clock Gear Filter: Image has zero width or height.");
        return canvas; // Return empty (or minimally sized) canvas
    }

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // --- Helper function to draw a single gear ---
    function drawGearInstance(context, x, y, outerRadius, teeth, toothDepth, holeRadiusVal, colorToUse, rotation) {
        const innerRadius = outerRadius - toothDepth;
        const angleStep = Math.PI / teeth; // Angle for one segment (half tooth or half valley)

        context.save(); // Save current transform and style state
        context.translate(x, y); // Move origin to gear center
        context.rotate(rotation); // Rotate gear

        context.beginPath();
        // Move to the tip of the first tooth (aligned with the new x-axis after rotation)
        context.moveTo(outerRadius, 0); // outerRadius * cos(0), outerRadius * sin(0)

        for (let i = 0; i < teeth * 2; i++) {
            const currentAngle = (i + 1) * angleStep;
            // Alternate between inner and outer radius for vertices
            const r = (i % 2 === 0) ? innerRadius : outerRadius;
            context.lineTo(r * Math.cos(currentAngle), r * Math.sin(currentAngle));
        }
        context.closePath(); // Connect last point to the starting point

        context.fillStyle = colorToUse;
        context.fill();

        // Punch out a central hole if holeRadiusVal is significant
        // Check holeRadiusVal against a small epsilon relative to outerRadius
        if (holeRadiusVal > (0.001 * outerRadius)) {
            context.beginPath();
            // Arc for the hole, centered at (0,0) due to translation
            context.arc(0, 0, holeRadiusVal, 0, 2 * Math.PI, false);

            const prevCompositeOp = context.globalCompositeOperation;
            context.globalCompositeOperation = 'destination-out'; // New shapes erase existing content
            context.fillStyle = "rgba(0,0,0,1)"; // Must be opaque to punch out effectively
            context.fill();
            context.globalCompositeOperation = prevCompositeOp; // Restore original composite operation
        }

        context.restore(); // Restore transform and style state
    }

    // --- Gear Generation and Placement ---
    const baseDimension = Math.min(canvas.width, canvas.height); // Base size for relative calculations

    for (let i = 0; i < pNumGears; i++) {
        // Determine properties for this specific gear instance
        const radiusFactor = pAvgGearRadiusFactor + (Math.random() - 0.5) * 2 * pGearRadiusVariationFactor;
        let currentOuterRadius = baseDimension * Math.max(0.01, radiusFactor); // Ensure radius is positive

        // Skip drawing if gear is too small to be visible or meaningful
        if (currentOuterRadius < 1) { // Adjust threshold as needed (e.g., 0.5px)
            continue;
        }

        const currentTeethCount = Math.floor(Math.random() * (pMaxTeeth - pMinTeeth + 1)) + pMinTeeth;
        const currentToothDepthVal = currentOuterRadius * pToothDepthFactor;
        const currentHoleRadiusVal = currentOuterRadius * pHoleSizeFactor;
        const randomRotationVal = Math.random() * 2 * Math.PI; // Random rotation for each gear

        // Determine gear placement: gears can be partially off-canvas.
        // Center can range from (-0.5 * radius) to (canvas_dim + 0.5 * radius)
        const placementWidth = canvas.width + currentOuterRadius;
        const placementHeight = canvas.height + currentOuterRadius;
        const gx_center = Math.random() * placementWidth - (currentOuterRadius * 0.5);
        const gy_center = Math.random() * placementHeight - (currentOuterRadius * 0.5);

        drawGearInstance(ctx, gx_center, gy_center, currentOuterRadius, currentTeethCount, currentToothDepthVal, currentHoleRadiusVal, finalGearColor, randomRotationVal);
    }

    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Clock Gear Filter Effect tool allows users to overlay a clock gear filter onto an image, creating a unique artistic effect. Users can customize parameters, including the number of gears, their color, size, and tooth details, to generate a personalized gear pattern that enhances the visual appeal of the original image. This tool is ideal for graphic designers, digital artists, and hobbyists looking to add intricate mechanical designs to images for creative projects, social media graphics, or themed presentations.

Leave a Reply

Your email address will not be published. Required fields are marked *