# Laser Flow

A waterfall of light rendered with one WebGL2 shader. The beam drops in on mount, lands on the element you pass as `surface-ref`, runs along its top edge and spills over both corners. Smoke, dust and fine streaks flow with the light. Move the cursor over it to brighten the light around the pointer and reveal an image of your UI through the glow. Pick a preset in the demo panel, or press refresh to replay the intro.

<demo-laser-flow>



</demo-laser-flow>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import LaserFlow from '~/components/ui/laser-flow/LaserFlow.vue';

  const card = ref<HTMLElement | null>(null);
</script>

<template>
  <div class="relative h-125 overflow-hidden bg-black">
    <LaserFlow
      class="absolute inset-0"
      :surface-ref="card"
      color="#3847ff"
      :beam-position="0.6"
      reveal-image="/ui-outline.svg"
    />
    <div
      ref="card"
      class="absolute inset-x-[4%] top-[55%] -bottom-6 rounded-[14px] border bg-card"
    >
      <!-- your UI -->
    </div>
  </div>
</template>
```

Without `surface-ref` the beam lands on an invisible floor at `surface-level`. The surface's top corner radius is read from its computed style, so the light follows rounded corners. If the beam falls outside the surface, it lands on the floor instead.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        surface-ref
      </code>
    </td>
    
    <td>
      <code>
        HTMLElement | null
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      Element the beam lands on. The light spreads along its top edge and spills over its corners.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        beam-position
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Horizontal position of the beam, from 0 (left edge) to 1 (right edge).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        surface-level
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Where the beam lands when no <code>
        surface-ref
      </code>
      
       is given, from 0 (top) to 1 (bottom).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3847ff'
      </code>
    </td>
    
    <td>
      Color of the light. Every other tone is derived from it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.8
      </code>
    </td>
    
    <td>
      Overall brightness of the light.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        beam-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Thickness of the beam and its glow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flare
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Size of the splash where the beam meets the surface.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spread
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      How far the light travels along the surface.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spill
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How much light pours over the corners of the surface.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fog
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      Amount of smoke lit up by the beam. 0 turns it off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dust
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.6
      </code>
    </td>
    
    <td>
      Amount of glittering dust flowing with the light.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        streaks
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Brightness of the fine lines flowing along the splash.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dots
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Strength of the fine dot texture in the glow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pulse
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      How much the splash breathes over time.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Animation speed. 0 holds the light still.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        seed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Changes the shape of the smoke.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-interaction
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Brightens the light, smoke and dust softly around the cursor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-image
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Image revealed through the light around the cursor, such as a screenshot of your UI.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        220
      </code>
    </td>
    
    <td>
      Radius of the reveal around the cursor, in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-opacity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Strength of the revealed image. 0 hides it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Drops the beam in and lets the light run along the surface on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        theme
      </code>
    </td>
    
    <td>
      <code>
        'dark' | 'light'
      </code>
    </td>
    
    <td>
      <code>
        'dark'
      </code>
    </td>
    
    <td>
      Use <code>
        light
      </code>
      
       on light page backgrounds.
    </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 (`aria-hidden`) and the container ignores pointer events, so content under and around it stays clickable.
- With `prefers-reduced-motion: reduce` the intro is skipped, the light holds still and the cursor glow is off.
- The render loop stops when the canvas is offscreen or the tab is hidden, and idles once the light has settled while `paused`.
- The cross-origin `reveal-image` must allow CORS, since it is uploaded to WebGL.
- Ported from [React Bits](https://reactbits.dev/animations/laser-flow) by David Haz.
