A canvas grid of small squares that flicker at random brightness, like a busy status board. Pixel-snapped, token-colored, DPR aware, pauses off-screen, static under reduced motion.
Every node, every second.
Live health for 12,000 machines, streamed to one screen without a refresh.
import { FlickeringGrid } from "@/components/ballmac/flickering-grid"
export default function FlickeringGridDemo() {
return (
<div className="relative isolate flex h-[360px] w-full max-w-2xl flex-col items-center justify-center overflow-hidden rounded-xl border bg-background px-6 text-center">
<FlickeringGrid className="-z-10" maxOpacity={0.22} />
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(ellipse_40%_35%_at_50%_50%,var(--background),transparent)]"
/>
<span className="font-mono text-xs tracking-widest text-muted-foreground uppercase">Realtime</span>
<h2 className="mt-3 max-w-md text-4xl font-semibold tracking-tight text-balance sm:text-5xl">
Every node, every second.
</h2>
<p className="mt-3 max-w-sm text-sm text-muted-foreground">
Live health for 12,000 machines, streamed to one screen without a refresh.
</p>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/flickering-gridAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/colorCopy the source into your project.
components/ballmac/flickering-grid.tsx// Ballmac UI: Flickering Grid. https://ui.ballmac.com/components/flickering-grid "use client" import * as React from "react" import { cn } from "@/lib/utils" import { observeTheme, resolveCssColor } from "@/lib/ballmac/color" type FlickeringGridProps = Omit<React.ComponentProps<"div">, "children" | "color"> & { /** Side of each square in pixels. */ squareSize?: number /** Space between squares in pixels. */ gap?: number /** Chance per second that a square picks a new brightness (0–1+). */ flickerChance?: number /** Brightest a square gets (0–1). */ maxOpacity?: number /** Square color: a CSS variable name ("--foreground") or any CSS color. */ color?: string /** Fade the grid out toward the edges with a radial mask. */ fade?: boolean } function FlickeringGrid({ squareSize = 4, gap = 6, flickerChance = 0.3, maxOpacity = 0.3, color = "--foreground", fade = true, className, style, ...props }: FlickeringGridProps) { const rootRef = React.useRef<HTMLDivElement>(null) const canvasRef = React.useRef<HTMLCanvasElement>(null) React.useEffect(() => { const root = rootRef.current const canvas = canvasRef.current const ctx = canvas?.getContext("2d") if (!root || !canvas || !ctx) return const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches const pitch = squareSize + gap let width = 0 let height = 0 let cols = 0 let rows = 0 let dpr = 1 let opacities = new Float32Array(0) let fill = resolveCssColor(root, color) const resize = () => { dpr = Math.min(window.devicePixelRatio || 1, 2) width = root.clientWidth height = root.clientHeight canvas.width = Math.round(width * dpr) canvas.height = Math.round(height * dpr) canvas.style.width = `${width}px` canvas.style.height = `${height}px` cols = Math.ceil(width / pitch) rows = Math.ceil(height / pitch) opacities = new Float32Array(cols * rows) for (let i = 0; i < opacities.length; i++) opacities[i] = Math.random() * maxOpacity } const draw = () => { // Draw in device pixels and snap squares to whole pixels so they stay crisp. ctx.setTransform(1, 0, 0, 1, 0, 0) ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.fillStyle = fill const size = Math.max(1, Math.round(squareSize * dpr)) for (let c = 0; c < cols; c++) { const x = Math.round(c * pitch * dpr) for (let r = 0; r < rows; r++) { ctx.globalAlpha = opacities[c * rows + r]! ctx.fillRect(x, Math.round(r * pitch * dpr), size, size) } } ctx.globalAlpha = 1 } let frame = 0 let last = 0 let visible = true const loop = (now: number) => { const dt = last ? Math.min((now - last) / 1000, 0.1) : 0 last = now const chance = flickerChance * dt for (let i = 0; i < opacities.length; i++) { if (Math.random() < chance) opacities[i] = Math.random() * maxOpacity } draw() frame = requestAnimationFrame(loop) } const sync = () => { cancelAnimationFrame(frame) last = 0 if (reduceMotion) draw() else if (visible && !document.hidden) frame = requestAnimationFrame(loop) } resize() sync() const ro = new ResizeObserver(() => { resize() draw() }) ro.observe(root) const io = new IntersectionObserver(([entry]) => { visible = entry?.isIntersecting ?? true sync() }) io.observe(root) const stopTheme = observeTheme(() => { fill = resolveCssColor(root, color) draw() }) document.addEventListener("visibilitychange", sync) return () => { cancelAnimationFrame(frame) ro.disconnect() io.disconnect() stopTheme() document.removeEventListener("visibilitychange", sync) } }, [squareSize, gap, flickerChance, maxOpacity, color]) const mask = fade ? "radial-gradient(ellipse 70% 65% at 50% 50%, black 30%, transparent 80%)" : undefined return ( <div ref={rootRef} aria-hidden="true" data-slot="flickering-grid" className={cn("pointer-events-none absolute inset-0 overflow-hidden", className)} style={{ maskImage: mask, WebkitMaskImage: mask, ...style }} {...props} > <canvas ref={canvasRef} className="block" /> </div> ) } export { FlickeringGrid, type FlickeringGridProps }Update the import paths to match your project setup.
Usage
import { FlickeringGrid } from "@/components/ballmac/flickering-grid"The full example is in the Code tab above.
Examples
Accent card
Requests today
48.2M
Up 12% from yesterday
import { FlickeringGrid } from "@/components/ballmac/flickering-grid"
export default function FlickeringGridAccent() {
return (
<div className="relative isolate w-full max-w-sm overflow-hidden rounded-xl border bg-card p-6">
<FlickeringGrid color="--chart-1" squareSize={3} gap={4} maxOpacity={0.5} flickerChance={0.6} fade={false} className="-z-10 [mask-image:linear-gradient(to_top,black,transparent_75%)]" />
<p className="text-sm text-muted-foreground">Requests today</p>
<p className="mt-1 text-4xl font-semibold tracking-tight tabular-nums">48.2M</p>
<p className="mt-16 text-xs text-muted-foreground">Up 12% from yesterday</p>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
squareSizeSide of each square in pixels. | number | 4 |
gapSpace between squares in pixels. | number | 6 |
flickerChanceChance per second that a square picks a new brightness (0–1+). | number | 0.3 |
maxOpacityBrightest a square gets (0–1). | number | 0.3 |
colorSquare color: a CSS variable name ("--foreground") or any CSS color. | string | "--foreground" |
fadeFade the grid out toward the edges with a radial mask. | boolean | true |
Also accepts the standard attributes of its root element.
Use with AI
Place <FlickeringGrid /> as the first child of a relative, overflow-hidden container; it fills it (absolute inset-0, aria-hidden, pointer-events-none). Tune squareSize, gap and maxOpacity for density and contrast. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Flickering Grid (@ballmac/flickering-grid) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Section or hero backgrounds for infrastructure, data or developer products
- Feature cards that should feel alive without a focal animation
Not for
- Behind small or low-contrast text (keep maxOpacity low or add a scrim)
- Very large full-page areas on low-power devices (use dot-pattern)
Registry JSON: https://ui.ballmac.com/r/flickering-grid.json
Credits
Free to use in personal and commercial projects.
- npm
- None
- Registry
- @ballmac/colorshadcn/utils
Pairs well with
Number Ticker
Counts up to a number when it scrolls into view, with locale-aware formatting for currency, percentages and decimals.
Shimmer Text
Text with a band of light sweeping across it, from muted to full foreground color. Suited to AI "thinking" and loading status lines; static under reduced motion.
Aurora Background
Soft, slowly drifting aurora light made of blurred token-colored glows with fine vertical curtains and an optional radial fade. Pure CSS layers, paused off-screen, still under reduced motion.
Beams Background
Hairline rails with thin light beams rising along them and dissolving near the top, over a soft glow. Canvas, token colors, DPR aware, pauses off-screen, a still frame under reduced motion.