# Grainient

> Flowing grainy gradients you can stir with the cursor.

A grainy gradient that bends into slow waves on raw WebGL2. Moving the cursor drags the colors around like wet paint, and clicking twists them into a swirl that slowly unwinds. Turn up `bands` for stripes or rings, drop `softness` for crisp poster edges, or add `silk` for a satin sheen.

<demo-grainient>



</demo-grainient>

## Installation

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

## Usage

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

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <Grainient
      :colors="['#06101f', '#0c4a6e', '#0284c7', '#7dd3fc', '#f0f9ff']"
      :angle="0"
      :bands="3"
      :softness="0.25"
      :silk="0.3"
    />
  </div>
</template>
```

Colors are blended in OKLab, so mixes stay vivid instead of turning muddy. The gradient is rendered at half resolution and the grain is added at full resolution on top. Rendering stops when nothing moves, when the tab is hidden, and while the background is scrolled out of view.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        colors
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        ['#120f17', '#2a0f0a', '#9a3412', '#f97316', '#fed7aa']
      </code>
    </td>
    
    <td>
      Two to eight CSS colors, from the base to the highlight. Use your page background as the first color so edges blend.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        type
      </code>
    </td>
    
    <td>
      <code>
        'linear' | 'radial'
      </code>
    </td>
    
    <td>
      <code>
        'linear'
      </code>
    </td>
    
    <td>
      Linear runs the colors across the container. Radial puts the last color in the middle.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        angle
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        55
      </code>
    </td>
    
    <td>
      Direction of a linear gradient in degrees, like CSS: 0 runs bottom to top, 90 left to right.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bands
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How many times the colors repeat back and forth, from 1 to 8. Higher values make stripes or rings.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        softness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Blend between colors, from 0 (crisp, poster-like edges) to 1 (smooth).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.8
      </code>
    </td>
    
    <td>
      How much the gradient bends into waves, from 0 (straight) to 3. Above 1 it starts to marble.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Size of the waves, from 0.25 to 3.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Speed of the drifting waves. 0 holds them still.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        silk
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.35
      </code>
    </td>
    
    <td>
      Satin shading on the folds, from 0 to 1. Combine with bands for glossy ribbons.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain-amount
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.08
      </code>
    </td>
    
    <td>
      Strength of the film grain, from 0 to 0.5.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.2
      </code>
    </td>
    
    <td>
      Size of the grain in pixels, from 0.5 to 4.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grain-animated
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Shifts the grain every frame like film. When off, the grain stays fixed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contrast
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Contrast of the colors, from 0.2 to 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        saturation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Saturation of the colors, from 0 (grayscale) to 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Picks a different pattern of waves.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        interactive
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets the cursor drag the colors around like paint. The trail settles back after a moment.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stir
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How far the cursor drags the colors, from 0 to 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-swirl
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking twists the colors into a swirl that slowly unwinds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Blooms the colors out from the middle on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        light-mode
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Builds a light version of the palette: the first color becomes white and the others run in reverse, deepest last.
    </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 `aria-hidden`. Put real content on top of it in your own markup.
- Pointer input is read from `window`, so content layered on top stays fully interactive while the cursor still stirs the colors.
- With `prefers-reduced-motion: reduce` the waves and grain hold still and the cursor stir, click swirl and intro are turned off.
- Ported from [React Bits](https://reactbits.dev/backgrounds/grainient) by David Haz.
