Skip to content
Sign in
EffectsASCII wave loaderMenu

ASCII wave loaderPro

An ASCII luminance ramp animated as a wave field inside a fixed size block, for terminal style waits: a sandbox booting, a container pulling, a model loading.

ProA terminal style wait while a sandbox boots, updated on 60 ms ticks in a fixed size block.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/ascii-wave-loader

Usage

tsx
import { AsciiWaveLoader } from "@/components/ui/chatcn/pro/ascii-wave-loader"

<AsciiWaveLoader label="Booting the sandbox" cols={56} rows={14} pattern="ripple" />

Each 60 ms tick writes the frame straight into the text node of a <pre>, so React does not re-render 16 times a second. The block is sized in character cells (cols by rows) before the first frame, so nothing around it ever shifts. Pass your own ramp, light to dense with an empty first character, for a different texture.

Examples

Patterns

Swell, ripple, tide and noise, a paused loader and right to left.

ProSwell, ripple, tide and noise and a paused loader.

Props

PropTypeNotes
labelReactNodeWhat is happening, under the field with the shimmer
ariaLabelstringAccessible name when label is not text. Default "Loading"
childrenReactNodeExtra content under the label
cols, rowsnumberSize in character cells. Default 48 by 12
pattern"swell" | "ripple" | "tide" | "noise"Default swell
rampstringLight to dense glyphs, first one empty. Default .,:-=+*#%@
ticknumberMs per frame. Default 60
speed, seed, stillnumberDefault 1, 5 and 1.5 s
delaynumberWait before anything shows. Default 300 ms
paused, defaultPaused, onPausedChangeControlled or uncontrolled pause
pauseControlbooleanShow the pause button. Default true
preClassNamestringClasses for the <pre>, for example a text size

Keyboard

KeyAction
TabReaches the pause button
Enter / SpacePauses or plays the wave

Accessibility

  • The root is role=status with your label as its accessible name. The <pre> is aria-hidden, so a screen reader never reads the glyphs.
  • Content you pass as children is not announced on every tick.
  • The pause button has a name, a 32 px hit area and 44 px on touch screens.

Motion

  • Nothing shows for the first 300 ms, so a fast load shows no loader at all. Once it is visible it stays at least 400 ms, so it never flashes.
  • The loop stops offscreen and in a background tab, and the pause button in the corner stops it for good. Paused time does not count, so play continues where it left off.
  • Under reduced motion one still frame holds and the label stops shimmering. Nothing moves.