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.
On this page
ProA terminal style wait while a sandbox boots, updated on 60 ms ticks in a fixed size block.
Installation
Usage
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
| Prop | Type | Notes |
|---|---|---|
label | ReactNode | What is happening, under the field with the shimmer |
ariaLabel | string | Accessible name when label is not text. Default "Loading" |
children | ReactNode | Extra content under the label |
cols, rows | number | Size in character cells. Default 48 by 12 |
pattern | "swell" | "ripple" | "tide" | "noise" | Default swell |
ramp | string | Light to dense glyphs, first one empty. Default .,:-=+*#%@ |
tick | number | Ms per frame. Default 60 |
speed, seed, still | number | Default 1, 5 and 1.5 s |
delay | number | Wait before anything shows. Default 300 ms |
paused, defaultPaused, onPausedChange | Controlled or uncontrolled pause | |
pauseControl | boolean | Show the pause button. Default true |
preClassName | string | Classes for the <pre>, for example a text size |
Keyboard
| Key | Action |
|---|---|
Tab | Reaches the pause button |
Enter / Space | Pauses or plays the wave |
Accessibility
- The root is
role=statuswith your label as its accessible name. The<pre>isaria-hidden, so a screen reader never reads the glyphs. - Content you pass as
childrenis 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.