# ASCII Effect

> Responsive image-to-ASCII renderer on a 2D canvas with three variants — a static image, a flowing wave field with a pointer ripple, and a signal glitch with a radial reveal.

ASCII Effect samples an image into a grid of characters, dithers the luminance, and draws it on a canvas that resizes with its container. The `flow` variant drifts the characters in a wave and ripples them around the pointer; the `glitch` variant reveals the image from the center and tears random rows sideways.

<demo-ascii-effect>



</demo-ascii-effect>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import AsciiEffect from '~/components/ui/ascii-effect/AsciiEffect.vue';
</script>

<template>
  <div class="relative h-130 overflow-hidden rounded-2xl">
    <AsciiEffect
      variant="flow"
      image-src="/images/portrait.webp"
      :font-size="6"
      :scale="1"
      :flow-speed="0.22"
      :flow-strength="12"
      :mouse-radius="150"
      :mouse-strength="22"
    />
  </div>
</template>
```

The component fills its parent, so give the parent a height. The image is read with `getImageData`, so remote images must be served with CORS headers.

### Variant presets

`AsciiEffectImage`, `AsciiEffectFlow` and `AsciiEffectGlitch` take every prop except `variant` and default `colors` to a variant palette (grey, cyan–indigo, lime–cyan).

```vue
<script setup lang="ts">
  import AsciiEffectGlitch from '~/components/ui/ascii-effect/AsciiEffectGlitch.vue';
</script>

<template>
  <div class="relative h-130 overflow-hidden">
    <AsciiEffectGlitch
      image-src="/images/portrait.webp"
      :glitch-intensity="0.65"
      :glitch-frequency="1.4"
      :reveal-duration="1400"
    />
  </div>
</template>
```

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        image-src
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      required
    </td>
    
    <td>
      Image URL rendered as ASCII characters
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variant
      </code>
    </td>
    
    <td>
      <code>
        'image' | 'flow' | 'glitch'
      </code>
    </td>
    
    <td>
      <code>
        'image'
      </code>
    </td>
    
    <td>
      Visual effect variation
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        chars
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ' .:-=+*#%@'
      </code>
    </td>
    
    <td>
      Characters ordered from darkest to brightest
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        9
      </code>
    </td>
    
    <td>
      Character height in pixels
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-family
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Arial, Helvetica, sans-serif'
      </code>
    </td>
    
    <td>
      Font stack used to draw the characters
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-weight
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Canvas font weight
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        line-height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Character row height multiplier
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        character-spacing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Horizontal character-cell spacing multiplier
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        brightness-boost
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2.2
      </code>
    </td>
    
    <td>
      Multiplier applied to sampled image luminance
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contrast
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.1
      </code>
    </td>
    
    <td>
      Contrast applied before character selection
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threshold
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.06
      </code>
    </td>
    
    <td>
      Dark-pixel cutoff that keeps the background clean
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        posterize
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        32
      </code>
    </td>
    
    <td>
      Number of luminance steps
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dither
      </code>
    </td>
    
    <td>
      <code>
        'none' | 'floyd-steinberg' | 'bayer'
      </code>
    </td>
    
    <td>
      <code>
        'floyd-steinberg'
      </code>
    </td>
    
    <td>
      Dithering algorithm used to keep photographic detail
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dither-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.8
      </code>
    </td>
    
    <td>
      Amount of error diffusion or ordered dithering
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.22
      </code>
    </td>
    
    <td>
      Flow cycles per second (<code>
        flow
      </code>
      
       variant)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow-direction
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Flow direction in degrees
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Directional displacement in pixels
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        flow-frequency
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.018
      </code>
    </td>
    
    <td>
      Size of the flowing wave field
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        150
      </code>
    </td>
    
    <td>
      Radius of the pointer ripple in pixels
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        22
      </code>
    </td>
    
    <td>
      Pointer ripple displacement in pixels
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-wave-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.2
      </code>
    </td>
    
    <td>
      Speed of the pointer ripple
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.15
      </code>
    </td>
    
    <td>
      Image zoom inside the ASCII field
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fit
      </code>
    </td>
    
    <td>
      <code>
        'cover' | 'contain' | 'stretch'
      </code>
    </td>
    
    <td>
      <code>
        'cover'
      </code>
    </td>
    
    <td>
      How the source image fits the canvas
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        colors
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        ['#f4f4f5', '#a1a1aa']
      </code>
    </td>
    
    <td>
      Hex colors used as a luminance gradient
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color-mode
      </code>
    </td>
    
    <td>
      <code>
        'gradient' | 'source'
      </code>
    </td>
    
    <td>
      <code>
        'gradient'
      </code>
    </td>
    
    <td>
      Use the gradient or colors sampled from the image
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#07090d'
      </code>
    </td>
    
    <td>
      Canvas background color
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        invert
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Reverse the luminance-to-character mapping
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glitch-intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.65
      </code>
    </td>
    
    <td>
      Strength and number of displaced row bands (<code>
        glitch
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        glitch-frequency
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.4
      </code>
    </td>
    
    <td>
      Approximate glitches per second
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1400
      </code>
    </td>
    
    <td>
      Radial reveal duration in milliseconds (<code>
        glitch
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        alt
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'ASCII rendering'
      </code>
    </td>
    
    <td>
      Accessible label for the canvas
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Additional classes for the container
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas has `role="img"` and uses `alt` as its accessible name. Describe the image, not the effect.
- With `prefers-reduced-motion: reduce`, the flow and glitch variants render one static frame: no drift, no ripple, no glitch bands, no reveal.
- The animation loop pauses while the component is off screen.
- The pointer ripple is decoration only; nothing depends on it.
- Ported from [componentry.fun](https://componentry.dev/docs/components/ascii-effect) by Harsh Jadhav.
