# Pixel Trail

> Glossy pixels melt into a fluid goo trail behind the cursor, with color shifts, drips, sparkle, click bursts, dither and image reveals.

A WebGL2 trail of crisp, glossy pixels follows the cursor. With the gooey filter on, neighbouring pixels melt into one liquid blob with bevels, highlights and a soft glow; with it off you get separate squares, dots or diamonds that fade, shrink or dither away. Move around the demo, and click when a preset has click bursts on.

<demo-pixel-trail>



</demo-pixel-trail>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/pixel-trail.json"
```

## Usage

The trail fills its parent and ignores pointer events, so content under it stays clickable.

```vue
<script setup lang="ts">
  import PixelTrail from '~/components/ui/pixel-trail/PixelTrail.vue';
</script>

<template>
  <div class="relative h-100 w-full overflow-hidden">
    <PixelTrail
      :grid-size="50"
      :max-age="500"
      color="#A855F7"
      trail-color="#22D3EE"
      :gooey-filter="{ strength: 3 }"
      :gravity="0.6"
      click-burst
    />
  </div>
</template>
```

The cursor paints soft brush strokes into an offscreen 512×512 canvas. A single fragment shader samples it on a `grid-size` grid and, when goo is on, blurs the cells with an analytic Gaussian so they merge into one shaded surface. Pass `image-src` to make each pixel take the color of the image beneath it (the image must allow CORS).

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        grid-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        40
      </code>
    </td>
    
    <td>
      Number of pixels across the longer side
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trail-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.1
      </code>
    </td>
    
    <td>
      Brush radius, as a fraction of the longer side
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-age
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        250
      </code>
    </td>
    
    <td>
      How long each part of the trail lasts, in ms
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        interpolate
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      In-between points on fast moves; higher is smoother
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        easing-function
      </code>
    </td>
    
    <td>
      <code>
        (x: number) => number
      </code>
    </td>
    
    <td>
      <code>
        x => x
      </code>
    </td>
    
    <td>
      Curve for how the trail swells in and fades out
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Pixel color
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trail-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Second color the trail shifts to as it ages
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gooey-filter
      </code>
    </td>
    
    <td>
      <code>
        { strength?: number } | false
      </code>
    </td>
    
    <td>
      <code>
        { strength: 2 }
      </code>
    </td>
    
    <td>
      Melts pixels into liquid blobs; higher is rounder. <code>
        false
      </code>
      
       for no goo
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'square' | 'circle' | 'diamond'
      </code>
    </td>
    
    <td>
      <code>
        'square'
      </code>
    </td>
    
    <td>
      Pixel shape when goo is off
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.08
      </code>
    </td>
    
    <td>
      Space between pixels (0–0.9); draws fine seams when goo is on
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        decay
      </code>
    </td>
    
    <td>
      <code>
        'fade' | 'shrink' | 'dither'
      </code>
    </td>
    
    <td>
      <code>
        'fade'
      </code>
    </td>
    
    <td>
      How pixels disappear
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      How much the trail keeps drifting in the cursor's direction (0–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gravity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Trail sags down as it fades; negative values rise (-1–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sparkle
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Makes fading pixels flicker (0–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-burst
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Clicking splashes a ring of trail out from the cursor
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shine
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Bevels, brighter core and highlights (0–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      Soft glow around the trail (0–1)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image-src
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Image the trail reveals
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dpr
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      auto (max 2)
    </td>
    
    <td>
      Device pixel ratio for the canvas
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the container
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is `aria-hidden` and the layer has `pointer-events: none`.
- With `prefers-reduced-motion: reduce`, `flow`, `gravity` and `sparkle` are turned off, so the trail only follows the pointer.
- Rendering stops when the trail has faded or the container is off screen.
- Ported from [React Bits](https://reactbits.dev/animations/pixel-trail) by David Haz.
