Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/ascii-video.jsonUsage
import { AsciiVideo } from "@/components/asciicn/ascii-video";
export function Reel() {
return (
<AsciiVideo
src="/media/reel.mp4"
alt="Our product reel"
charset="dense"
className="aspect-video w-full"
/>
);
}Each frame is drawn into a small canvas at the size of the glyph grid and area-averaged into one tone and colour per sample. A 120 by 50 grid reads about 6,000 samples per frame, so sampling stays cheap even at 60 fps. fps caps how often a new frame is sampled; between samples nothing redraws.
The video plays muted and inline, so browsers allow autoplay. Pause stops sampling and holds the last frame.
src takes a URL or a File from an <input type="file"> or a drop. Files on another origin need CORS headers, or the browser won't let the canvas read their pixels.
Camera
The camera button (or webcam) swaps the video for the visitor's camera, mirrored like a selfie view. Browsers only offer the camera in a secure context: https:// or localhost. On plain http://, such as a dev server reached by its LAN or Tailscale address, the box says so instead of failing silently. The camera turns off when the button is pressed again or the component unmounts.
Sound
With audioReactive, a sound button appears. Turning it on unmutes the soundtrack (or opens the microphone, with the camera) and lets it move the glyphs. The bass raises contrast, the overall level lifts brightness and the treble shifts random rows sideways for a frame. reactivity scales all three.
Examples
Your own file
Pick any video on your machine. It stays in the browser: nothing is uploaded.
Camera in phosphor Braille
Press the camera button. With sound on, the microphone drives the glyphs.
Props
Every ASCII image look prop also applies.
| Prop | Type | Default |
|---|---|---|
| src A video URL, or a File the visitor picked. | Type: string | Blob | Default – |
| alt What the video shows, read by screen readers. | Type: string | Default – |
| webcam Start with the camera instead of src. | Type: boolean | Default false |
| mirror Flip left to right. | Type: boolean | Default true for the camera, false for files |
| autoPlay Play muted on load. Off under reduced motion. | Type: boolean | Default true |
| controls Show the play, camera and sound buttons. | Type: boolean | Default true |
| audioReactive Add a sound button that lets audio drive contrast, brightness and row jitter. | Type: boolean | Default false |
| audio Where the sound comes from. | Type: "track" | "mic" | Default "track", or "mic" with the camera |
| reactivity How strongly sound moves the glyphs, 0 to 1. | Type: number | Default 0.6 |
| charset Which glyphs draw the frames. | Type: CharsetName | Default "standard" |
| colorMode How glyphs are coloured. | Type: "mono" | "truecolor" | "ansi16" | "duotone" | "palette" | Default "truecolor" |
| fit How the video fills its box. | Type: "cover" | "contain" | "fill" | Default "cover" |
| autoLevels Stretch every frame's tones to the whole ramp. Helps dim webcams. | Type: boolean | Default false |
| fps Frames sampled per second, 12 to 60. | Type: number | Default 30 |
| cell Glyph cell height in CSS pixels. | Type: number | Default 10 |
| handle Filled with a handle for export, including video recording from the canvas. | Type: RefObject<AsciiHandle | null> | Default – |
Notes
- The glyphs are labelled as an image with
alt. The buttons sit outside that image, so screen readers reach them, and each says whether it is on. - Under reduced motion the video loads paused on its first frame. Play still works.
- Sampling pauses when the video is offscreen or the tab is hidden; playback itself continues.
- The sample clip is NASA footage of the Apollo 11 launch (public domain) with a synthesised rumble added for the sound demo.