Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/balloon-valley.jsonUsage
import { BalloonValley } from "@/components/asciicn/balloon-valley";
export function Hero() {
return (
<section className="relative isolate min-h-[480px]">
<BalloonValley className="absolute inset-0 -z-10" />
<h1>Your headline</h1>
</section>
);
}It is early morning. The sky is a soft blue overhead, paling to cream in the middle and pale peach at the horizon, and a gold sun sits half over the farthest ridge in a warm glow. Four ridges in cool blue shadow step down towards you and open in a V under the sun. Each is paler than the one in front, sinking into the haze, and the faces turned to the sun warm up. Pale gold mist pools in the low ground of every V. At the bottom, two blue hills frame a sage valley floor, where the river winds towards you as a pale ribbon with gold glints on it.
Five hot-air balloons hang in the sky at different depths. The nearest is large and striped red and gold, lit from the sun's side and glowing through its shaded side. The far ones are smaller and fade into the haze. Two of them rise and sink over a few minutes, and the low one drops behind the ridges and comes back up.
The scene is printed like ink on warm cream paper: the dot grows with how far a colour is from the paper. The pale sky is the faintest dots, so the middle of the box is almost bare paper. The cool blue hills and the gold mist are small and medium dots, and only the sun, the river's glints and the balloons get large ones. bayer4 dithering, the default, mixes neighbouring dot sizes so gradients stay smooth. square cells, on by default, lay the dots on an even lattice like a halftone screen, about 200 columns across a 1440px screen at the default 7px cell. In truecolor, the default, the scene prints on its own cream paper, so it looks the same in light and dark themes. Put dark text on it. duotone with a ramp and mono with a phosphor read the same scene by tone alone.
The motion is slow: mist drifts along the valley, the river's glints shimmer and breathe, and the balloons bob. Nothing reacts to the pointer.
The land sits in the lower third, so a centred headline lands on quiet sky with the sun just below it. On wide boxes the sun sits a little right of centre and the balloons keep to the sides and the top. On tall boxes the balloons gather above the headline and the sun and the river sit below it.
Examples
Hero
A centred headline and two buttons over the full scene at full intensity. The headline is a dark slate blue taken from the hills and lands on almost bare cream; a soft wash of the paper behind it keeps it clean.
Duotone print
colorMode="duotone" with a four-stop ramp draws the valley in rust and slate on the cream, like a two-ink print. cell={6} adds detail in a smaller card.
Props
| Prop | Type | Default |
|---|---|---|
| intensity How strongly the valley draws, from 0 to 1. Lower values fade it back into its paper. | Type: number | Default 1 |
| paper The surface behind the glyphs. Without it, truecolor, ansi16 and duotone print on a warm cream; 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 " ·∙•●". 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 the faintest dots to the strongest. The default runs from the paper's cream to slate-blue ink. | Type: RampName | string[] | Default cream to slate |
| 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 |
| edgeThreshold Edge charset: how strong a change in tone must be to draw - / | or \. | Type: number | Default 0.22 |
| dither Ordered dithering between neighbouring dot sizes. The default turns four dots into smooth tone. | Type: "none" | "bayer2" | "bayer4" | "bayer8" | "blue-noise" | Default "bayer4" |
| 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, and its width too with square cells. | Type: number | Default 7 |
| square Square cells, so the dots lie on an even lattice. false uses the font's narrower cells. | Type: boolean | Default true |
| speed How fast the mist, the river and the balloons 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. At 0 the low balloon is part way up from behind the ridges. | 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 dawn gradient.
- The whole valley is one shader pass on the GPU, one sample per cell in
dots. Mist and ridge texture use a few octaves of noise, so it costs a little more than a flat gradient. See Performance.