# Dot Grid

> A springy dot grid that the cursor flicks and drags through like jelly, with click shockwaves.

A grid of springy dots drawn with instanced WebGL2. The dots pop in from the center when the grid appears. Move the cursor through it to flick and drag the dots; they streak as they move, light up, wobble back into place and pass the ripple to their neighbours. Click to send a shockwave out across the grid.

<demo-dot-grid>



</demo-dot-grid>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import DotGrid from '~/components/ui/dot-grid/DotGrid.vue';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <DotGrid
      base-color="#3a3446"
      active-color="#ffffff"
      :dot-size="5"
      :gap="18"
      :proximity="140"
    />
  </div>
</template>
```

Each dot is a damped spring tied to its rest position. With `tension` above
0 every dot also pulls on its four neighbours, so a push travels through the
grid like jelly. The simulation sub-steps as needed to stay stable, and the
loop stops as soon as every dot has settled.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        base-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3a3446'
      </code>
    </td>
    
    <td>
      Color of the dots at rest. Hex only.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        active-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Color the dots light up to while they move or sit near the cursor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'circle' | 'square'
      </code>
    </td>
    
    <td>
      <code>
        'circle'
      </code>
    </td>
    
    <td>
      Shape of each dot.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dot-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      Size of each dot, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gap
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        18
      </code>
    </td>
    
    <td>
      Space between dots, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        proximity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        140
      </code>
    </td>
    
    <td>
      Radius around the cursor that pushes the dots and lights them up, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How hard a moving cursor pushes the dots.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bounce
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      How much the dots overshoot and wobble as they settle, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tension
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How strongly each dot pulls on its neighbours. 0 keeps them independent.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        return-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.8
      </code>
    </td>
    
    <td>
      Roughly how long the dots take to settle back into place, in seconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shock-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        320
      </code>
    </td>
    
    <td>
      How far a click shockwave travels, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shock-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      How hard a click shockwave pushes the dots.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        swell
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.8
      </code>
    </td>
    
    <td>
      How much the dots grow while they are lit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How much moving dots stretch into short streaks along their motion.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Soft halo around lit dots.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fade
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Fades the grid out toward the edges, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        opacity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Overall opacity of the grid.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Pops the dots in from the center when the grid first appears.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-interaction
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets the cursor push the dots and light them up.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-shock
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Sends a shockwave out from each click.
    </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 for the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative, marked `aria-hidden="true"` and ignores pointer events, so content on top stays clickable.
- With `prefers-reduced-motion: reduce` the dots stay still: no intro, no cursor push, no streaks and no shockwaves.
- Pointer tracking is passive and never blocks scrolling.
- Ported from [React Bits](https://reactbits.dev/backgrounds/dot-grid) by David Haz.
