Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/london.jsonUsage
import { London } from "@/components/asciicn/london";
export function Hero() {
return (
<section className="relative isolate min-h-[480px]">
<London intensity={0.85} className="absolute inset-0 -z-10" />
<h1>Your headline</h1>
</section>
);
}The scene is a 3D model of Westminster, raymarched from a camera on the South Bank just south of Westminster Bridge and then painted in halftone dots. Every dot takes its colour from the scene's palette of about 45 hand-picked colours, and its size from how bright that point is, so the sky reads as fine specks and the floodlit stone as full dots.
The Elizabeth Tower stands right of centre, built to its real proportions: a panelled shaft 12 m square, the clock stage with a dial on each face, the open belfry lit from inside, and the iron spire with its corner pinnacles and the Ayrton light, 96 m to the finial. The camera sees it a little off square, so the floodlit east face carries the big glowing dial and the north face beside it stands in shade with a sliver of its own dial. The Palace of Westminster runs left from the tower's foot and recedes along the river: buttresses every few metres, each ending in a pinnacle, rows of windows with a few lit, pavilions with corner turrets, and steep slate roofs, with the Central Tower's spire in the middle and the Victoria Tower at the far end, paler and hazier with distance. Westminster Bridge crosses the river beyond the tower on low arches, its lamp standards receding towards the far bank.
The light is modelled too. Floodlights from the south-east light the river front and the tower's east faces, and the buttresses and piers throw their own shade. Behind it all the sky runs from pale gold low in the west through rose to slate blue overhead, with a few stars. Haze thickens with distance, so the far end of the palace stands back.
The Thames reflects the scene by tracing the mirrored ray from every point on the water. Slow ripples tip that ray, so the gold of the palace and the tower breaks into long dashes, darker and cooler than the real thing, with a thin streak under each lamp.
Motion is slow: the ripples move, haze and mist drift, the lamps and the dials breathe softly, a few windows turn on or off every minute or so, the clock hands keep time and one river cruiser passes the palace and goes under the bridge at walking pace. The pointer does nothing.
The tower stands right of the middle in every box. On a wide screen the headline sits over the sky with the buttons along the gold band above the roofline. On a phone the view widens so the whole tower stays in frame beside the nearest part of the palace.
It draws in dots by default on square 7px cells, about 200 across a 1440px screen, with bayer4 dithering between dot sizes. square={false} returns to the usual text cells, twice as tall as wide; braille and quadrants use text cells unless you ask for square ones. Other ramp charsets such as standard or dense read the same tones as typed glyphs. In truecolor, the default, the scene paints its own night blue paper in both themes. mono with a phosphor and duotone with a ramp read the same scene by tone alone.
intensity dims the colours in step and shrinks the dots more gently, so the shapes keep their place behind text.
Examples
Hero
intensity={0.85} quiets the scene a little behind a centred headline and two buttons. The headline sits over the sky and the tower stands to its right.
Thick mist
fog={1} thickens the haze, so the far end of the palace fades, and lays mist along the river. cell={6} packs finer dots into a small card and boat={false} leaves the river empty.
Props
| Prop | Type | Default |
|---|---|---|
| intensity How strongly the city draws, from 0 to 1. Lower it to keep text on top readable. | Type: number | Default 1 |
| fog How much haze hangs over the city and mist over the river, from 0 (clear) to 1. | Type: number | Default 0.5 |
| boat The river cruiser heading downstream past the palace and under the bridge. | Type: boolean | Default true |
| paper The surface behind the glyphs. Without it, truecolor, ansi16 and duotone sit on a night 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. Default "sodium". | Type: RampName | string[] | Default – |
| 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 dot sizes, so gradients read as a smooth halftone. Ramp charsets only; braille and quadrants default to none. | 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. | Type: number | Default 7 |
| square Cells as wide as they are tall, so the dots lie in an even lattice. On by default for one-glyph charsets such as dots, off for braille and quadrants. | Type: boolean | Default – |
| speed How fast it moves. | 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 24 |
| stillTime The moment drawn when motion is off or reduced, in seconds. | Type: number | Default 30 |
| 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
stillTime, with the boat on the river, and 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 dusk gradient.
- The whole city is one shader pass on the GPU that raymarches the model for every cell. Water cells trace a second, reflected ray, so the river costs about twice as much as the buildings, and rays that clear every roof stop at once, so the open sky is cheap.
braillesamples eight times per cell andquadrantsfour. See Performance.