This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Background ASCII Dither
ns-ui/background-ascii-ditherRemovedComponent
Luminance-to-glyph canvas renderer: ASCII, Bayer-dither, and dot-matrix modes with cursor-proximity resolve.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/background-ascii-dither.jsonSource
1"use client";23import { useEffect, useRef } from "react";45const ASCII_RAMP = " .:-=+*#%@";6// 4x4 Bayer matrix, normalized 0..17const BAYER = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5].map(8 (v) => (v + 0.5) / 169);1011type Mode = "ascii" | "dither" | "dot";1213// cheap flowing value-noise, no deps14function noise(x: number, y: number, t: number): number {15 const v =16 Math.sin(x * 1.7 + t * 0.6) +17 Math.sin(y * 2.3 - t * 0.4) +18 Math.sin((x + y) * 1.1 + t * 0.25) +19 Math.sin(Math.hypot(x - 6, y - 4) * 1.9 - t * 0.7);20 return v / 8 + 0.5; // 0..121}2223export function AsciiDitherMedia({24 mode = "ascii",25 src,26 cellSize = 14,27 cursorRadius = 140,28 className = "",29}: {30 mode?: Mode;31 /** optional image URL; omit for the built-in animated noise field */32 src?: string;33 /** grid cell size in px */34 cellSize?: number;35 /** cursor-proximity resolve radius in px */36 cursorRadius?: number;37 className?: string;38}) {39 const canvasRef = useRef<HTMLCanvasElement>(null);4041 useEffect(() => {42 const canvas = canvasRef.current;43 if (!canvas) return;44 const ctx = canvas.getContext("2d");45 if (!ctx) return;4647 const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;48 // glyph color from the surrounding theme; canvas bg stays transparent49 const fg = getComputedStyle(canvas).color;5051 let cols = 0;52 let rows = 0;53 let dpr = 1;54 let imageLum: Float32Array | null = null;55 const cursor = { x: -1e4, y: -1e4, tx: -1e4, ty: -1e4 };56 let raf = 0;57 let t = 0;58 // bumped on every sampleImage() dispatch so a late-resolving onload from59 // a superseded resize (rapid window-resize) can detect it's stale and60 // bail instead of writing imageLum sized to an out-of-date cols/rows61 let sampleGen = 0;6263 const resize = () => {64 dpr = Math.min(window.devicePixelRatio || 1, 2);65 const { width, height } = canvas.getBoundingClientRect();66 canvas.width = width * dpr;67 canvas.height = height * dpr;68 cols = Math.ceil(width / cellSize);69 rows = Math.ceil(height / cellSize);70 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);71 ctx.font = `${cellSize * 0.85}px "GeistMono", ui-monospace, monospace`;72 ctx.textAlign = "center";73 ctx.textBaseline = "middle";74 if (src) sampleImage();75 };7677 // coalesce bursts of resize events (dragging a window edge fires dozens)78 // into one recompute + one image refetch/redecode79// … truncated
