# Ballpit

> A soft-lit pit of physics balls you can shove with the cursor, grab, throw and blast apart.

Hundreds of ray-traced balls settle into a pit with real contact physics. Move the pointer to push them aside with a lit cursor ball, press on a ball to grab and throw it, or click an empty spot to blast the balls nearby away with a flash of light.

<demo-ballpit>



</demo-ballpit>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import Ballpit from '~/components/ui/ballpit/Ballpit.vue';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <Ballpit
      :count="200"
      :colors="['#ffffff', '#5227ff']"
      material="gloss"
      :gravity="0.5"
      click-effect="burst"
    />
  </div>
</template>
```

The component fills its parent, so give the parent a height. It renders with raw WebGL2: each ball is a single quad that is ray-traced in the fragment shader, so thousands of balls stay smooth. The animation loop sleeps once the balls come to rest and wakes on input or prop changes.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        count
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Number of balls in the pit (1–4000).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        colors
      </code>
    </td>
    
    <td>
      <code>
        (string | number)[]
      </code>
    </td>
    
    <td>
      <code>
        ['#ffffff']
      </code>
    </td>
    
    <td>
      Ball colors, handed out in turn. Hex strings or numbers such as <code>
        0x5227ff
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        material
      </code>
    </td>
    
    <td>
      <code>
        'satin' | 'matte' | 'gloss' | 'frosted' | 'metal'
      </code>
    </td>
    
    <td>
      <code>
        'satin'
      </code>
    </td>
    
    <td>
      Surface finish: soft satin, chalky matte, polished gloss, see-through frosted or metal.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        min-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Smallest ball radius. The view is about 18 units tall.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Largest ball radius.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gravity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Pull toward the floor. <code>
        0
      </code>
      
       lets the balls float, around <code>
        0.5
      </code>
      
       piles them up.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        friction
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.9975
      </code>
    </td>
    
    <td>
      How much speed the balls keep each frame. <code>
        1
      </code>
      
       never slows them down.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wall-bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.95
      </code>
    </td>
    
    <td>
      How much speed the balls keep when they hit the edges, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.85
      </code>
    </td>
    
    <td>
      How bouncy the balls are against each other, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        depth
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      How deep the pit is, so balls can sit in front of and behind each other.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        drift
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Gentle wandering that keeps floating balls moving on their own.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        follow-cursor
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      A ball follows the cursor and shoves the others out of the way.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cursor-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Radius of the cursor ball.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grab
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Press on a ball to pick it up, drag it around and throw it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-effect
      </code>
    </td>
    
    <td>
      <code>
        'burst' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'burst'
      </code>
    </td>
    
    <td>
      Clicking an empty spot blasts the nearby balls away with a flash of light.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        light-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color of the light that follows the cursor and lights up the balls around it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        light-intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Brightness of the cursor light.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exposure
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Overall brightness of the balls.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadows
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Strength of the soft shadows where balls touch each other and the floor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Balls pour in from the top on mount, or burst out from the center when gravity is near zero.
    </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 for the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative and marked `aria-hidden`. Put real content above it.
- Pointer input is read on the window, so content layered over the pit still gets its own clicks: presses on links, buttons, inputs and labels never grab a ball or trigger a burst.
- With `prefers-reduced-motion: reduce`, the intro, drift and click burst are turned off and balls start at rest.
- The loop pauses when the pit is offscreen or the tab is hidden.
- Ported from [React Bits](https://reactbits.dev/backgrounds/ballpit) by David Haz.
