pythoneventscursorplaywrightplaywright-python

How to Create a Mouse Trail Effect in Playwright for Random Mouse Movements?


I'm using Playwright to automate browser interactions, and would like to create a visual effect where the mouse leaves a trail as it moves randomly across the screen, mimicking natural cursor movement. I want to add a simple cursor trail effect in which a small dot or circle follows the mouse's position as it moves.

I found a reference, where it does not resolve my problem. Here is my code:

import asyncio
import random
from playwright.async_api import async_playwright, Page


async def move_cursor_randomly(page: Page, duration: int = 10):
    """
    Moves cursor randomly within the viewport for the specified duration.
    """
    viewport_size = page.viewport_size
    width = viewport_size["width"]
    height = viewport_size["height"]

    end_time = asyncio.get_event_loop().time() + duration
    while asyncio.get_event_loop().time() < end_time:
        x = random.randint(0, width)
        y = random.randint(0, height)

        await page.mouse.move(x, y, steps=random.randint(10, 30))

        await draw_cursor_trail(page, x, y)

        await asyncio.sleep(random.uniform(1, 3)) # to mimic randomness


async def draw_cursor_trail(page: Page, x, y):
    """
    supposed to draw a cursor trail at the specified coordinates.
    """
    await page.evaluate("""
        (x, y) => {
            const trail = document.createElement('div');
            trail.style.position = 'absolute';
            trail.style.left = `${x}px`;
            trail.style.top = `${y}px`;
            trail.style.width = '10px';
            trail.style.height = '10px';
            trail.style.borderRadius = '50%';
            trail.style.backgroundColor = 'blue';
            trail.style.zIndex = '9999';
            document.body.appendChild(trail);

            setTimeout(() => {
                trail.remove();
            }, 1000);
        }
    """, arg=(x, y))

However, when I run this, the trail doesn't appear as expected, and the mouse movement seems to be jumpy or not smooth. Which does not matter right now as we are more focused on making the trail appear. P.S -> there is a blue dot at the top of the screen blinking and i suspect that is the trail. If so then why it is not following the cursor. I am too confused rn!!


Solution

  • Based on @ggorlen suggestion and a bunch of retry i finally get it to work. This is the base code that generates a red dot at where your mouse is. It will continue to follow your mouse where ever you take it and after 300 msec the red dot fade away, thus leaving a trail.

    await page.evaluate(
            """
                // First we create the styles
                const style = document.createElement('style');
                style.innerHTML = `
                    .cursor-trail {
                        position: fixed;
                        width: 10px;  /* Size */
                        height: 10px;
                        background-color: red;  /* Color */
                        border-radius: 50%;
                        pointer-events: none;
                        z-index: 10000;
                        opacity: 0.5;
                        transition: opacity 0.3s, transform 0.3s;
                    }
                `;
                document.head.appendChild(style);
    
                // Then we append an event listener for the trail
                document.addEventListener('mousemove', (event) => {
                    const trailDot = document.createElement('div');
                    trailDot.classList.add('cursor-trail');
                    document.body.appendChild(trailDot);
    
                    trailDot.style.left = `${event.clientX}px`;
                    trailDot.style.top = `${event.clientY}px`;
    
                    // after 300ms we fade out and remove the trail dot
                    setTimeout(() => {
                        trailDot.style.opacity = '0';
                        setTimeout(() => trailDot.remove(), 300);
                    }, 50);
                });
            """
        )
    

    Then if you mix it with a function like this, playwright moves your cursor randomly and the red dot follows it ->

    async def move_cursor_randomly(page: Page, duration: int = 10):
        """
        Moves the cursor randomly within the viewport for the specified duration.
        A cursor trail is drawn at each new cursor position.
        """
        viewport_size = page.viewport_size
        width = viewport_size["width"]
        height = viewport_size["height"]
    
        await page.evaluate(
            """
                // First we create the styles
                const style = document.createElement('style');
                style.innerHTML = `
                    .cursor-trail {
                        position: fixed;
                        width: 10px;  /* Size */
                        height: 10px;
                        background-color: red;  /* Color */
                        border-radius: 50%;
                        pointer-events: none;
                        z-index: 10000;
                        opacity: 0.5;
                        transition: opacity 0.3s, transform 0.3s;
                    }
                `;
                document.head.appendChild(style);
    
                // Then we append an event listener for the trail
                document.addEventListener('mousemove', (event) => {
                    const trailDot = document.createElement('div');
                    trailDot.classList.add('cursor-trail');
                    document.body.appendChild(trailDot);
    
                    trailDot.style.left = `${event.clientX}px`;
                    trailDot.style.top = `${event.clientY}px`;
    
                    // after 300ms we fade out and remove the trail dot
                    setTimeout(() => {
                        trailDot.style.opacity = '0';
                        setTimeout(() => trailDot.remove(), 300);
                    }, 50);
                });
            """
        )
    
        end_time = asyncio.get_event_loop().time() + duration
        while asyncio.get_event_loop().time() < end_time:
            x = min(random.randint(0, width), width)
            y = min(random.randint(0, height), height)
    
            await page.mouse.move(x, y, steps=random.randint(10, 30))
            await asyncio.sleep(random.uniform(1, 3))