Please bookmark this page to avoid losing your image tool!

Image Tennis Rounds Visualizer

(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.
async function processImage(originalImg, numRounds = 5, effectIntensity = 5, layout = 'grid') {

    // --- Helper Functions for YTP-style Effects ---

    /**
     * Applies a random hue rotation, saturation, and contrast change.
     * The `drawImage` call is necessary to bake the filter into the canvas pixels.
     */
    const applyColorShift = (ctx, width, height, intensity) => {
        const hue = (Math.random() - 0.5) * 45 * (intensity / 5);
        const saturate = 100 + Math.random() * 20 * intensity;
        const contrast = 100 + Math.random() * 10 * intensity;
        
        ctx.save();
        ctx.filter = `hue-rotate(${hue}deg) saturate(${saturate}%) contrast(${contrast}%)`;
        ctx.drawImage(ctx.canvas, 0, 0);
        ctx.restore();
    };

    /**
     * Inverts the colors of the canvas.
     */
    const applyInvert = (ctx, width, height, intensity) => {
        if (Math.random() < 0.1 * intensity) { // Only apply this sometimes as it's very strong
             ctx.save();
             ctx.filter = 'invert(1)';
             ctx.drawImage(ctx.canvas, 0, 0);
             ctx.restore();
        }
    };
    
    /**
     * Pixelates the image. This is done manually by sampling and redrawing blocks.
     */
    const applyPixelate = (ctx, width, height, intensity) => {
        const size = Math.max(2, Math.floor(intensity * 1.5 * Math.random()));
        
        for (let y = 0; y < height; y += size) {
            for (let x = 0; x < width; x += size) {
                const pixel = ctx.getImageData(x, y, 1, 1).data;
                ctx.fillStyle = `rgba(${pixel[0]}, ${pixel[1]}, ${pixel[2]}, ${pixel[3] / 255})`;
                ctx.fillRect(x, y, size, size);
            }
        }
    };
    
    /**
     * Mirrors one half of the image over the other.
     */
    const applyMirror = (ctx, width, height, intensity) => {
        ctx.save();
        if (Math.random() > 0.5) { // Horizontal mirror
            const halfWidth = Math.ceil(width / 2);
            ctx.drawImage(ctx.canvas, 0, 0, halfWidth, height, 0, 0, halfWidth, height); // Draw left half
            ctx.scale(-1, 1);
            ctx.drawImage(ctx.canvas, 0, 0, halfWidth, height, -width, 0, halfWidth, height); // Draw mirrored left half on the right
        } else { // Vertical mirror
            const halfHeight = Math.ceil(height / 2);
            ctx.drawImage(ctx.canvas, 0, 0, width, halfHeight, 0, 0, width, halfHeight); // Draw top half
            ctx.scale(1, -1);
            ctx.drawImage(ctx.canvas, 0, 0, width, halfHeight, 0, -height, width, halfHeight); // Draw mirrored top half on the bottom
        }
        ctx.restore();
    };

    /**
     * Stutters or repeats a small section of the image nearby.
     */
    const applyStutter = (ctx, width, height, intensity) => {
        const sx = Math.random() * width * 0.7;
        const sy = Math.random() * height * 0.7;
        const sw = width * (0.1 + Math.random() * 0.3);
        const sh = height * (0.1 + Math.random() * 0.3);
        
        const numRepeats = 1 + Math.floor(Math.random() * (intensity / 2));
        for (let i = 0; i < numRepeats; i++) {
            const dx = sx + (Math.random() - 0.5) * 40;
            const dy = sy + (Math.random() - 0.5) * 40;
            ctx.drawImage(ctx.canvas, sx, sy, sw, sh, dx, dy, sw * (0.8 + Math.random() * 0.4), sh * (0.8 + Math.random() * 0.4));
        }
    };
    
    /**
     * Draws some classic "meme" text on the canvas.
     */
    const drawRandomText = (ctx, width, height, intensity) => {
        const texts = ['SUS', 'PINGAS', 'LOL', 'NOICE', 'BOI', 'YTP'];
        const text = texts[Math.floor(Math.random() * texts.length)];
        
        const fontSize = Math.floor(height / (8 - intensity / 2));
        ctx.font = `bold ${fontSize}px Impact, sans-serif`;
        ctx.fillStyle = 'white';
        ctx.strokeStyle = 'black';
        ctx.lineWidth = Math.max(2, fontSize / 12);

        const textMetrics = ctx.measureText(text);
        const x = Math.random() * (width - textMetrics.width);
        const y = Math.random() * (height - fontSize) + fontSize;

        ctx.strokeText(text, x, y);
        ctx.fillText(text, x, y);
    };

    /**
     * Selects and applies a random set of effects to a canvas context.
     */
    const applyYtpEffects = (ctx, width, height, intensity) => {
        const effects = [
            applyColorShift,
            applyInvert,
            applyPixelate,
            applyMirror,
            applyStutter,
            drawRandomText
        ];

        // Apply more effects for higher intensity
        const numEffectsToApply = 1 + Math.floor(Math.random() * (intensity / 2.5));

        for (let i = 0; i < numEffectsToApply; i++) {
            const randomEffect = effects[Math.floor(Math.random() * effects.length)];
            randomEffect(ctx, width, height, intensity);
        }
    };


    // --- Main Function Logic ---

    // 1. Sanitize parameters
    const safeNumRounds = Math.max(1, Math.min(20, Number(numRounds) || 5));
    const safeIntensity = Math.max(1, Math.min(10, Number(effectIntensity) || 5));

    // 2. Scale image for performance
    const MAX_DIMENSION = 400;
    let baseWidth = originalImg.width;
    let baseHeight = originalImg.height;
    if (baseWidth > MAX_DIMENSION || baseHeight > MAX_DIMENSION) {
        if (baseWidth > baseHeight) {
            baseHeight = Math.round(baseHeight * (MAX_DIMENSION / baseWidth));
            baseWidth = MAX_DIMENSION;
        } else {
            baseWidth = Math.round(baseWidth * (MAX_DIMENSION / baseHeight));
            baseHeight = MAX_DIMENSION;
        }
    }

    const firstCanvas = document.createElement('canvas');
    firstCanvas.width = baseWidth;
    firstCanvas.height = baseHeight;
    firstCanvas.getContext('2d').drawImage(originalImg, 0, 0, baseWidth, baseHeight);

    const canvases = [firstCanvas];

    // 3. Generate subsequent rounds by applying cumulative effects
    for (let i = 1; i < safeNumRounds; i++) {
        const prevCanvas = canvases[i - 1];
        const currentCanvas = document.createElement('canvas');
        currentCanvas.width = baseWidth;
        currentCanvas.height = baseHeight;
        const ctx = currentCanvas.getContext('2d');

        // Start with the previous round's image
        ctx.drawImage(prevCanvas, 0, 0);

        // Apply a new set of random effects
        applyYtpEffects(ctx, baseWidth, baseHeight, safeIntensity);

        canvases.push(currentCanvas);
    }

    // 4. Create the final display container and layout the canvases
    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.gap = '15px';
    container.style.flexWrap = 'wrap';
    container.style.justifyContent = 'center';
    container.style.fontFamily = 'Arial, sans-serif';
    container.style.color = '#333';
    
    if (layout === 'grid') {
        container.style.display = 'grid';
        let columns = Math.ceil(Math.sqrt(safeNumRounds));
        if (safeNumRounds <= 3) columns = safeNumRounds;
        container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
        container.style.maxWidth = `${columns * (baseWidth + 30)}px`;
    } else if (layout === 'vertical') {
        container.style.flexDirection = 'column';
        container.style.alignItems = 'center';
    } else { // 'horizontal' is the default fallback
        container.style.flexDirection = 'row';
    }

    canvases.forEach((canvas, index) => {
        const roundWrapper = document.createElement('div');
        roundWrapper.style.padding = '10px';
        roundWrapper.style.border = '1px solid #ccc';
        roundWrapper.style.borderRadius = '8px';
        roundWrapper.style.backgroundColor = '#f9f9f9';
        roundWrapper.style.textAlign = 'center';
        roundWrapper.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';

        const label = document.createElement('p');
        label.textContent = `Round ${index + 1}`;
        label.style.margin = '0 0 8px 0';
        label.style.fontWeight = 'bold';
        
        canvas.style.display = 'block';
        canvas.style.borderRadius = '4px';

        roundWrapper.appendChild(label);
        roundWrapper.appendChild(canvas);
        container.appendChild(roundWrapper);
    });

    return container;
}

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 Tennis Rounds Visualizer is an interactive online tool that allows users to apply a series of creative visual effects to an image in a sequence of rounds. Users can specify the number of rounds, the intensity of effects, and choose a layout for displaying the results. The tool utilizes various transformative effects such as color shifts, pixelation, mirroring, and random text additions to create unique stylized versions of the original image. This tool is ideal for those looking to generate fun and artistic iterations of images, such as for memes, social media content, or artistic projects.

Leave a Reply

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