# Blob Cursor

> A 3D liquid blob follows the cursor, pulls into a tail as it moves, sheds drips and bursts into droplets on click — in chrome, jelly, pearl or ink.

A smooth 3D liquid replaces the cursor inside its container. It stretches into a curving tail as you move, wobbles when it stops, swells over links and buttons, and bursts into droplets when you click — then grows back and soaks the droplets up again. Move and click in the demo.

<demo-blob-cursor>



</demo-blob-cursor>

## Installation

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

## Usage

Place the component inside a positioned container. It fills the container, ignores pointer events and hides the system cursor of its parent element.

```vue
<script setup lang="ts">
  import BlobCursor from '~/components/ui/blob-cursor/BlobCursor.vue';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <BlobCursor material="jelly" color="#ff4fb8" :drip="0.6" />
  </div>
</template>
```

The blob renders with three.js: the head, tail and merging droplets are one signed-distance field ray-marched on a single plane, lit by a `RoomEnvironment` map. Flying droplets are an instanced sphere mesh. Add `data-blob-grow` to any element to make the blob swell over it, like it does over links and buttons.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        material
      </code>
    </td>
    
    <td>
      <code>
        'chrome' | 'jelly' | 'pearl' | 'ink'
      </code>
    </td>
    
    <td>
      <code>
        'chrome'
      </code>
    </td>
    
    <td>
      Liquid metal, glossy gel, iridescent shell or flat goo
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3B82F6'
      </code>
    </td>
    
    <td>
      Tints chrome and pearl reflections, fills jelly and ink
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        blend
      </code>
    </td>
    
    <td>
      <code>
        'normal' | 'difference' | 'exclusion' | 'multiply' | 'screen' | 'overlay'
      </code>
    </td>
    
    <td>
      <code>
        'normal'
      </code>
    </td>
    
    <td>
      CSS <code>
        mix-blend-mode
      </code>
      
       of the blob layer
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        140
      </code>
    </td>
    
    <td>
      Diameter of the main blob in px (12–400)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        morph
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      0 = round bead, 1 = restless shapeshifting puddle
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        morph-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How fast the shape keeps changing
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        depth
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      0 = flat puddle, 1 = full round bead
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trail
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Links in the liquid tail (0–16)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        lag
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.55
      </code>
    </td>
    
    <td>
      0 = tight follow, 1 = slow and floaty
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        goo
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How syrupy the joins between head, tail and droplets are
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      How far the blob pulls into a teardrop while moving
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wobble
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Bounce when it stops, turns or gets clicked
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        drip
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Droplets flung off the tail on fast moves; they dry up or get soaked back
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        splash
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking bursts droplets out of the blob
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        splash-count
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        6
      </code>
    </td>
    
    <td>
      Droplets per click (1–32)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        splash-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      0 = gentle spill, 1 = hard burst
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        splash-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Droplet size relative to the blob
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        splash-gravity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Pulls flying droplets down into an arc
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        regrow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How quickly the blob regains its size after a splash
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shine
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Strength of highlights and reflections
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Casts a soft shadow under the blob
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hide-cursor
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Hides the system cursor on the parent element
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root element
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is `aria-hidden` and the layer has `pointer-events: none`, so it never blocks clicks or focus.
- With `prefers-reduced-motion: reduce` the blob follows the pointer tightly, does not morph, drip or burst on click.
- Rendering stops when the blob is idle or the container is off screen.
- Keep `hide-cursor` off on surfaces where users need a precise pointer.
- Ported from [React Bits](https://reactbits.dev/animations/blob-cursor) by David Haz.
