# Meta Balls

> Liquid metaballs that merge, stretch and pinch apart, with a cursor blob that pushes through them.

Liquid blobs orbit, drift or rise like a lava lamp, clinging together, stretching as they move and wobbling when they snap apart. A cursor blob follows your pointer and pushes through them, and a click splashes them outward before they flow back. Pick filled, outline or contour styles.

<demo-meta-balls>



</demo-meta-balls>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { MetaBalls } from '~/components/ui/meta-balls';
</script>

<template>
  <div class="relative h-120 w-full">
    <MetaBalls
      color="#ff5a1f"
      cursor-ball-color="#ff5a1f"
      motion="lava"
      :animation-size="16"
      :ball-count="9"
      enable-transparency
    />
  </div>
</template>
```

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the blobs.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cursor-ball-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the cursor blob. It blends smoothly into any blob it merges with.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variant
      </code>
    </td>
    
    <td>
      <code>
        'solid' | 'outline' | 'contour'
      </code>
    </td>
    
    <td>
      <code>
        'solid'
      </code>
    </td>
    
    <td>
      Filled blobs, a single outline, or an outline with contour rings around it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        motion
      </code>
    </td>
    
    <td>
      <code>
        'orbit' | 'drift' | 'lava'
      </code>
    </td>
    
    <td>
      <code>
        'orbit'
      </code>
    </td>
    
    <td>
      Orbit a cluster, drift across the area, or rise and sink like a lava lamp.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Speed of the motion.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animation-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Size of the world shown in the view. Higher values zoom out and make the blobs smaller.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ball-count
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        15
      </code>
    </td>
    
    <td>
      Number of blobs, up to 50.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clump-factor
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How far the blobs travel from the center. Lower values pack them together.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gooeyness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How far blobs reach toward each other before merging, from 0 to 1. 0.5 is the classic look.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stickiness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How strongly merged blobs cling together before the neck between them snaps, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How much blobs stretch in the direction they move, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wobble
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How much blobs jiggle after stretching or being pushed, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        enable-mouse-interaction
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      The cursor blob follows the pointer and pushes the others aside. It orbits on its own otherwise.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cursor-ball-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Size of the cursor blob.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-smoothness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.1
      </code>
    </td>
    
    <td>
      How quickly the cursor blob catches up with the pointer. Lower is smoother.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-burst
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking pulses the cursor blob and splashes the others outward before they flow back.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Width of the lines in the outline and contour variants, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rings
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        4
      </code>
    </td>
    
    <td>
      Number of contour rings around the outline.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Starts as a single drop that buds into the blobs.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        enable-transparency
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Renders on a transparent background instead of black.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paused
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Freezes the animation.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dpr
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      auto
    </td>
    
    <td>
      Maximum device pixel ratio, capped at 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes merged on the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative and marked `aria-hidden`; it never captures focus or blocks clicks.
- The intro is skipped when `prefers-reduced-motion: reduce` is set. Use `paused` to freeze the motion entirely.
- The render loop stops while the component is scrolled out of view.
- Needs WebGL2; without it the container stays empty.
- Ported from [React Bits](https://reactbits.dev/animations/meta-balls) by David Haz.
