Please bookmark this page to avoid losing your image tool!

Image Pinwheel Generator

(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, spinSpeed = 4, showStick = 1) {
    const speed = isNaN(parseFloat(spinSpeed)) ? 4 : parseFloat(spinSpeed);
    const stickVisible = parseInt(showStick, 10) !== 0;

    const maxCanvasSize = 800; // Cap resolution
    let size = Math.min(originalImg.width, originalImg.height);
    if (size > maxCanvasSize) {
        size = maxCanvasSize;
    }
    const R = size / 2;

    // Center crop the original image
    const sqCanvas = document.createElement("canvas");
    sqCanvas.width = size;
    sqCanvas.height = size;
    const sqCtx = sqCanvas.getContext("2d");
    const minDim = Math.min(originalImg.width, originalImg.height);
    const sx = (originalImg.width - minDim) / 2;
    const sy = (originalImg.height - minDim) / 2;
    
    sqCtx.imageSmoothingEnabled = true;
    sqCtx.imageSmoothingQuality = "high";
    sqCtx.drawImage(originalImg, sx, sy, minDim, minDim, 0, 0, size, size);
    
    const img = sqCanvas;

    const canvasW = 2.4 * R;
    const canvasH = stickVisible ? 3.4 * R : 2.4 * R;
    const cx = canvasW / 2;
    const cy = 1.2 * R;

    // Create main responsive wrapper DOM
    const wrapper = document.createElement("div");
    wrapper.style.position = "relative";
    wrapper.style.width = canvasW + "px";
    wrapper.style.height = canvasH + "px";
    wrapper.style.display = "inline-block";

    function createLayer() {
        const cvs = document.createElement("canvas");
        cvs.width = canvasW;
        cvs.height = canvasH;
        cvs.style.position = "absolute";
        cvs.style.top = "0";
        cvs.style.left = "0";
        return cvs;
    }

    // 1. Draw static background stick
    if (stickVisible) {
        const stickCanvas = createLayer();
        const sCtx = stickCanvas.getContext("2d");
        sCtx.translate(cx, cy);
        
        const stickW = R * 0.08;
        const stickL = R * 2.2;
        
        const stickGrad = sCtx.createLinearGradient(-stickW/2, 0, stickW/2, 0);
        stickGrad.addColorStop(0, "#4a2e1b"); 
        stickGrad.addColorStop(0.3, "#CD853F"); 
        stickGrad.addColorStop(0.7, "#DEB887"); 
        stickGrad.addColorStop(1, "#8B4513"); 
        
        sCtx.fillStyle = stickGrad;
        sCtx.fillRect(-stickW/2, 0, stickW, stickL);
        
        const stickShadow = sCtx.createLinearGradient(0, 0, 0, R * 0.4);
        stickShadow.addColorStop(0, "rgba(0,0,0,0.7)");
        stickShadow.addColorStop(1, "transparent");
        sCtx.fillStyle = stickShadow;
        sCtx.fillRect(-stickW/2, 0, stickW, stickL);
        
        wrapper.appendChild(stickCanvas);
    }

    // 2. Spinner Container (Applies static drop shadow while content rotates)
    const spinnerDiv = document.createElement("div");
    spinnerDiv.style.position = "absolute";
    spinnerDiv.style.top = "0";
    spinnerDiv.style.left = "0";
    spinnerDiv.style.width = "100%";
    spinnerDiv.style.height = "100%";
    spinnerDiv.style.filter = `drop-shadow(${R*0.04}px ${R*0.06}px ${R*0.05}px rgba(0,0,0,0.5))`;

    // 3. Draw the Pinwheel
    const wheelCanvas = createLayer();
    const wCtx = wheelCanvas.getContext("2d");
    wCtx.translate(cx, cy);
    wCtx.imageSmoothingEnabled = true;
    wCtx.imageSmoothingQuality = "high";

    // Draw the 4 flat base cuts of the folded paper
    for (let i = 0; i < 4; i++) {
        wCtx.save();
        wCtx.rotate((Math.PI / 2) * i);

        wCtx.beginPath();
        wCtx.moveTo(0, 0);
        wCtx.lineTo(-R, -R);
        wCtx.lineTo(0, -R);
        wCtx.lineTo(R / 2, -R / 2);
        wCtx.closePath();
        
        wCtx.clip();
        wCtx.drawImage(img, -R, -R, 2 * R, 2 * R);
        wCtx.restore();
    }

    // Draw the 4 overlapping folded flaps with 3D internal shadows
    for (let i = 0; i < 4; i++) {
        wCtx.save();
        const A = (Math.PI / 2) * i;
        wCtx.rotate(A);

        wCtx.beginPath();
        wCtx.moveTo(0, 0);
        wCtx.lineTo(0, -R);
        wCtx.lineTo(R / 2, -R / 2);
        wCtx.closePath();

        wCtx.save();
        wCtx.shadowColor = "rgba(0,0,0,0.7)";
        wCtx.shadowBlur = R * 0.08;
        
        // Dynamically rotate un-transformed shadow constants so shadow logic remains strictly bound to wheel axes
        const dx = R * 0.03;  
        const dy = R * 0.03;
        wCtx.shadowOffsetX = dx * Math.cos(A) - dy * Math.sin(A);
        wCtx.shadowOffsetY = dx * Math.sin(A) + dy * Math.cos(A);
        
        wCtx.fillStyle = "white"; 
        wCtx.fill();
        wCtx.restore();

        wCtx.clip();
        // Mirrors image over the folding crease exactly simulating paper reversing back-side
        wCtx.transform(0, 1, 1, 0, R, -R);
        wCtx.drawImage(img, -R, -R, 2 * R, 2 * R);
        
        wCtx.fillStyle = "rgba(0,0,0,0.2)"; 
        wCtx.fillRect(-R, -R, 2 * R, 2 * R);
        wCtx.restore();
    }

    // Animate the rotation
    if (speed > 0) {
        const animName = "spin-" + Math.random().toString(36).substr(2, 6);
        const style = document.createElement("style");
        style.textContent = `
            @keyframes ${animName} {
                from { transform: rotate(0deg); }
                to { transform: rotate(360deg); }
            }
        `;
        document.head.appendChild(style);
        wheelCanvas.style.transformOrigin = `${(cx / canvasW) * 100}% ${(cy / canvasH) * 100}%`;
        wheelCanvas.style.animation = `${animName} ${speed}s linear infinite`;
    }

    spinnerDiv.appendChild(wheelCanvas);
    wrapper.appendChild(spinnerDiv);

    // 4. Draw Center Lock Pin Cap (Stays fixed relative to pinwheel rotation)
    const pinCanvas = createLayer();
    const pCtx = pinCanvas.getContext("2d");
    pCtx.translate(cx, cy);
    pCtx.beginPath();
    pCtx.arc(0, 0, R * 0.06, 0, Math.PI * 2);
    
    const pinGrad = pCtx.createRadialGradient(R*0.015, -R*0.015, 0, 0, 0, R*0.06);
    pinGrad.addColorStop(0, "#FFFACD");
    pinGrad.addColorStop(0.3, "#FFD700");
    pinGrad.addColorStop(0.8, "#B8860B");
    pinGrad.addColorStop(1, "#8B6508");
    pCtx.fillStyle = pinGrad;
    pCtx.fill();
    
    pCtx.beginPath();
    pCtx.arc(0, 0, R * 0.06, 0, Math.PI * 2);
    pCtx.lineWidth = Math.max(1, R * 0.01);
    pCtx.strokeStyle = "rgba(0,0,0,0.5)";
    pCtx.stroke();

    wrapper.appendChild(pinCanvas);

    return wrapper;
}

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 Pinwheel Generator transforms your uploaded photos into an animated, rotating pinwheel graphic. The tool crops your image into a square and applies a 3D-style paper folding effect with realistic shadows and a central pin to simulate a physical toy. Users can customize the experience by adjusting the rotation speed and choosing whether to display a decorative stick beneath the pinwheel. This tool is ideal for creating playful digital decorations, unique social media content, or whimsical visual elements for websites and presentations.

Leave a Reply

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