# CRT Warp

> A blocky plasma glowing on a curved CRT, with real scanlines, a phosphor mask, afterglow and a power-on intro.

CRT Warp draws a low-resolution plasma on a curved glass tube, with scanlines, a phosphor mask, afterglow trails and bloom. The tube warms up into view, the cursor bends the picture like a magnet held to the glass, and a click changes the channel.

<demo-crt-warp>



</demo-crt-warp>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import CrtWarp from '~/components/ui/crt-warp/CrtWarp.vue';
</script>

<template>
  <div class="relative size-full min-h-80 overflow-hidden">
    <CrtWarp
      class="absolute inset-0 size-full"
      color="#c755f7"
      background-color="#120f17"
      mask="aperture"
      :speed="0.5"
      :columns="80"
      :curvature="0.25"
    />
  </div>
</template>
```

The demo presets (Ultraviolet, Amber, Oscilloscope, Arcade, Interference) ship with the component. `crtWarpPreset('amber')` returns the full set of visual props; pass `true` as the second argument for the light-background variant.

```vue
<script setup lang="ts">
  import { CrtWarp, crtWarpPreset } from '~/components/ui/crt-warp';

  const amber = crtWarpPreset('amber');
</script>

<template>
  <CrtWarp v-bind="amber" />
</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>
        '#c755f7'
      </code>
    </td>
    
    <td>
      Phosphor color of the picture.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#120f17'
      </code>
    </td>
    
    <td>
      Color of the dark tube behind the picture. Use <code>
        'transparent'
      </code>
      
       to let the page show through.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        light-mode
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Prints the picture as ink on a light background instead of glowing phosphor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mask
      </code>
    </td>
    
    <td>
      <code>
        'aperture' | 'shadow' | 'slot' | 'none'
      </code>
    </td>
    
    <td>
      <code>
        'aperture'
      </code>
    </td>
    
    <td>
      The phosphor pattern of the tube: vertical stripes, dot triads, slots, or a plain mono tube.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How fast the plasma flows.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Picks a different plasma pattern. Each whole number is a new one.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wave-amplitude
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      How tightly the plasma folds into bands.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wave-frequency
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2.5
      </code>
    </td>
    
    <td>
      How many blobs fit on screen.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        levels
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Number of brightness steps in the picture.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        columns
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        80
      </code>
    </td>
    
    <td>
      Width of the low resolution picture in blocks. Fewer columns make chunkier blocks.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        persistence
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How long the phosphor keeps glowing after the picture moves on, leaving soft trails.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        curvature
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.25
      </code>
    </td>
    
    <td>
      Bulge of the curved glass.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        corners
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Rounds the corners of the tube and shows its edge. 0 keeps the picture edge to edge.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scanline-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Depth of the dark gaps between scanlines. Bright lines widen and fill the gaps.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scanline-frequency
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Number of scanlines from top to bottom.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mask-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Visibility of the phosphor mask.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bloom
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Glow that spills from bright areas.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bloom-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How far the glow spreads.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rgb-shift
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.003
      </code>
    </td>
    
    <td>
      Misalignment of the red and blue beams, which fringes edges with color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        noise
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.1
      </code>
    </td>
    
    <td>
      Grain in the lit picture.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flicker
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.15
      </code>
    </td>
    
    <td>
      Brightness flicker of the tube.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        interference
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.2
      </code>
    </td>
    
    <td>
      A slow hum bar rolling down the screen and jitter along the lines.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        vignette
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Darkening toward the edges of the tube.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        brightness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Overall brightness, or ink strength in light mode.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Warms the tube up when it first comes into view: the plasma grows out of the dark and pulls into focus.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-react
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      The cursor acts like a magnet held to the glass, twisting the picture with color fringes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Strength of the magnet.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        change-on-click
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking changes the channel: the picture rolls through static and comes back with a new pattern.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dpr
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Maximum device pixel ratio of the canvas.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fps
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      Maximum frame rate.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paused
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Freezes the plasma.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra class names for the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative and marked `aria-hidden`, so screen readers skip it. Put real content on top of it.
- Clicks on links, buttons, inputs, labels and `[role="button"]` elements never flip the channel, so content placed over the tube keeps working.
- With `prefers-reduced-motion: reduce`, the plasma stops flowing, the intro, flicker and magnet are skipped, and a click changes the channel at once without the rolling static.
- Rendering pauses while the tube is offscreen or the tab is hidden, and stops redrawing once a paused picture has settled.
- Ported from [React Bits](https://reactbits.dev/backgrounds/crt-warp) by David Haz.
