Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/canyon-river.jsonUsage
import { CanyonRiver } from "@/components/asciicn/canyon-river";
export function Hero() {
return (
<section className="relative isolate min-h-[480px]">
<CanyonRiver intensity={0.6} className="absolute inset-0 -z-10" />
<h1>Your headline</h1>
</section>
);
}You look down a desert canyon from its rim in late golden hour. Eroded walls rise on both sides. Their rims are broken at several scales and notched where gullies cut them, the strata tilt and break at faults, gullies branch down from the rim with slanting tributaries, and rounded scree spreads at the foot of the middle wall. Each buttress splits into planes: on the sunward wall, terracotta and rust planes in warm sunlight beside shaded ones under a lit rim; on the far wall, blue shadow planes that catch the sky, with light reflected up from the river low down. Behind them a middle wall and two overlapping rows of buttes step back towards a hazy plateau on the horizon, each farther layer paler and softer. Each butte has its own tilted, chipped cap, weathered shoulders and an uneven talus skirt. The buttes stand at the sides, so the middle stays open.
The sun hangs low over the plateau in a soft halo. A river leaves through a notch in the plateau below it and winds towards you, showing through the gap in each wall in turn. It mirrors the sky: gold under the sun, clear blue where you look down into it. The sky runs from amber at the horizon through pale blue to a deeper blue overhead, with a few thin streaks of cloud lit gold underneath.
Only light moves. The light slides slowly along the rims, streaks of shimmer cross the water, the clouds drift and the glow breathes. The pointer does nothing.
On wide boxes the sun sits well right of centre, so the middle stays calm for a headline. On tall boxes the view narrows and the horizon rises, so on a phone the sun sits above a centred headline and the river runs down beside it.
It draws in dots by default: dot size follows brightness, and in truecolor every dot takes its own colour, so shadow and the high sky stay a quiet grain of small, dim dots and the largest dots are kept for the sun, its reflection and the sunlit rims. Cells are square by default, so the dots sit on an even halftone lattice about 200 columns across a 1440px screen. With square={false} the cells are tall again and dots lights every other column to keep the lattice even. The scene paints its own sky on a deep blue paper, so it looks the same on light and dark pages. Text charsets such as standard and dense give a typed look and read best with square={false}, and braille and quadrants light a dot only above half tone. mono with a phosphor and duotone with a ramp read the same scene by tone alone.
intensity dims the scene: colours fade in step with it and the dots shrink a little. It quiets the middle of the box more than the edges, so a centred headline reads first while the walls and the sky still frame it.
Examples
Behind a headline
intensity={0.65} pushes the canyon back, most of all in the middle, so a centred headline and two buttons read first.
On an amber monitor
colorMode="mono" with phosphor="p3" draws every glyph in one amber, so the canyon reads by tone alone. The standard charset, a 10px cell and fx scanlines, bloom and vignette finish the look.
Props
| Prop | Type | Default |
|---|---|---|
| intensity How strongly the canyon draws, from 0 to 1. Lower it to keep text on top readable. | Type: number | Default 1 |
| clouds Thin streaks of cloud drifting across the sky. | Type: boolean | Default true |
| paper The surface behind the glyphs. Without it, truecolor, ansi16 and duotone sit on a deep blue; mono and palette use the theme's paper or the phosphor's. | Type: AsciiColor | Default – |
| fallback Shown without WebGL and until the first frame. | Type: ReactNode | Default a gradient |
| charset Which glyphs draw the tones. See Charsets. | Type: CharsetName | Default "dots" |
| chars A custom ramp from empty to dense, such as " .oO@". Wins over charset. | Type: string | Default – |
| colorMode How glyphs are coloured. truecolor paints the scene's own colours; mono uses one ink or a phosphor; duotone runs tone through a ramp. | Type: "mono" | "truecolor" | "ansi16" | "duotone" | "palette" | Default "truecolor" |
| phosphor A CRT ink and glass. Setting one switches the default colour mode to mono. | Type: "p1" | "p3" | "ibm" | "blueprint" | Default – |
| ramp In duotone mode, the colour ramp from dark to light. The default draws gold on dark. | Type: RampName | string[] | Default "sodium" |
| ansi In ansi16 mode, which 16-colour table. | Type: "vga" | "xterm" | Default "vga" |
| grade Brightness, contrast, gamma and saturation applied to tones before glyphs are picked. | Type: Grade | Default – |
| invert Dense glyphs for dark tones instead of bright ones. | Type: boolean | Default false |
| threshold Braille and quadrants: the tone that lights a dot. | Type: number | Default 0.5 |
| dither Ordered dithering between neighbouring glyph levels. | Type: "none" | "bayer2" | "bayer4" | "bayer8" | "blue-noise" | Default "none" |
| square Cells as wide as they are tall, so dots lay an even lattice. Turn it off for text charsets. | Type: boolean | Default true |
| background "transparent" draws the glyphs only, over your own surface. | Type: "paper" | "transparent" | Default "paper" |
| fx CRT finish: scanlines, bloom, curvature, glitch, flicker and vignette, each 0 to 1. | Type: PostFx | Default – |
| cell Height of one glyph cell in CSS pixels. In dots, also the spacing of the dot grid. | Type: number | Default 7 |
| speed How fast the light, the water and the clouds move. | Type: number | Default 1 |
| animate false stops the motion: a still frame if it starts off, the current frame if it was moving. | Type: boolean | Default true |
| fps Frame rate cap. | Type: number | Default 30 |
| stillTime The moment drawn when motion is off or reduced, in seconds. | Type: number | Default 0 |
| handle Filled with a handle that reads back the current frame, for export to text, ANSI, SVG or PNG. | Type: RefObject<AsciiHandle | null> | Default – |
| className Size and position. The background fills this box. | Type: string | Default – |
Notes
- It is decorative and hidden from assistive technology. Put real text on top of it, not inside it.
- It ignores the pointer: no parallax and no hover effect.
- With reduced motion turned on, it draws one still frame at
stillTimeand stops. - It pauses when scrolled out of view or when the tab is hidden, and releases its WebGL context when far offscreen. Without WebGL it shows a flat gradient of the scene.
- The whole canyon is one shader pass on the GPU, one sample per cell in
dots.braillesamples eight times per cell andquadrantsfour. See Performance.