GitHub 0

Matrix

Previous Next

Retro dot-matrix LED display with circular cells and smooth animations. Supports static patterns, animated frame sequences, and VU meter mode.

Installation

npx shadcn-svelte@latest add https://sv11.ui.twango.dev/r/matrix.json

Usage

<script lang="ts">
	import { Matrix } from "$lib/registry/ui/matrix";
</script>
 
<Matrix />

Examples

Static Pattern

Render a single frame by passing pattern. The built-in digits preset ships as a Frame[] indexed by numeral.

<script lang="ts">
	import { Matrix, digits } from "$lib/registry/ui/matrix";
</script>
 
<Matrix rows={7} cols={5} pattern={digits[5]} ariaLabel="Number five" />

Animated Display

Pass a Frame[] to frames and the matrix steps through them at fps.

<script lang="ts">
	import { Matrix, wave } from "$lib/registry/ui/matrix";
</script>
 
<Matrix rows={7} cols={7} frames={wave} fps={20} loop ariaLabel="Wave animation" />

VU Meter

Set mode="vu" and pass per-column levels in [0, 1]. Each column fills from the bottom.

<Matrix
	rows={7}
	cols={12}
	mode="vu"
	levels={[0.1, 0.6, 0.9, 0.7, 0.4, 0.8, 0.5, 0.3, 0.6, 0.9, 0.5, 0.2]}
/>

Custom Pattern

Hand-author a Frame (a 2D array of brightness values) for bespoke glyphs.

<script lang="ts">
	import { Matrix, type Frame } from "$lib/registry/ui/matrix";
 
	const heart: Frame = [
		[0, 1, 1, 0, 1, 1, 0],
		[1, 1, 1, 1, 1, 1, 1],
		[1, 1, 1, 1, 1, 1, 1],
		[0, 1, 1, 1, 1, 1, 0],
		[0, 0, 1, 1, 1, 0, 0],
		[0, 0, 0, 1, 0, 0, 0],
	];
</script>
 
<Matrix
	rows={6}
	cols={7}
	pattern={heart}
	size={14}
	gap={2}
	palette={{ on: "hsl(0 84% 60%)", off: "hsl(0 84% 20%)" }}
/>

Custom Palette

Swap the on / off colors to theme the display. Classic phosphor green:

<Matrix
	rows={7}
	cols={7}
	frames={wave}
	fps={20}
	palette={{ on: "hsl(142 76% 36%)", off: "hsl(142 76% 10%)" }}
/>

Frame Change Callback

onFrame fires whenever the active frame index advances, useful for syncing external UI.

<script lang="ts">
	import { Matrix, loader } from "$lib/registry/ui/matrix";
 
	let currentFrame = $state(0);
</script>
 
<Matrix rows={7} cols={7} frames={loader} fps={12} onFrame={(i) => (currentFrame = i)} />
<p>Frame: {currentFrame}</p>

Live VU Meter

Feed levels from reactive state to animate the meter at render time.

<script lang="ts">
	import { Matrix } from "$lib/registry/ui/matrix";
 
	let levels = $state<number[]>(Array(12).fill(0));
 
	$effect(() => {
		const id = setInterval(() => {
			levels = Array.from({ length: 12 }, () => Math.random());
		}, 50);
		return () => clearInterval(id);
	});
</script>
 
<Matrix rows={7} cols={12} mode="vu" {levels} size={10} gap={2} />

Custom Animation

Build an animation by supplying your own frame array.

<script lang="ts">
	import { Matrix, type Frame } from "$lib/registry/ui/matrix";
 
	const frames: Frame[] = [
		[
			[1, 0, 1],
			[0, 1, 0],
			[1, 0, 1],
		],
		[
			[0, 1, 0],
			[1, 0, 1],
			[0, 1, 0],
		],
	];
</script>
 
<Matrix rows={3} cols={3} {frames} fps={2} loop />

API Reference

Prop Type Default Description
rows number Number of rows in the matrix grid.
cols number Number of columns in the matrix grid.
pattern? Frame Static pattern to display. A 2D array of brightness values in [0, 1]. When set, animation is disabled and frames is ignored.
frames? Frame[] Ordered frames to loop through for animation. Ignored when pattern is provided.
fps? number 12 Playback rate in frames per second when animating frames.
autoplay? boolean true Start animating automatically on mount. Ignored when a static pattern is provided.
loop? boolean true Loop the frame sequence. When false, animation halts on the last frame.
size? number 10 Cell diameter in pixels.
gap? number 2 Gap between cells in pixels.
palette? { on: string; off: string } on: "currentColor", off: "var(--muted-foreground)" CSS colors for active and inactive cells. Defaults map on to the current text color and off to the muted foreground token.
brightness? number 1 Global brightness multiplier applied to every cell, clamped to [0, 1].
ariaLabel? string ARIA label for the container. Falls back to "matrix display" when omitted.
onFrame? (index: number) => void Invoked whenever the active frame index changes during animation.
mode? MatrixMode "default" Rendering mode. "vu" reads levels each render to draw a bottom-anchored meter instead of frames or pattern.
levels? number[] Per-column level values in [0, 1] used when mode="vu". Ignored in other modes.
ref? HTMLDivElement | null $bindable(null) Bound reference to the root container element.

Notes

  • Ships with built-in presets: digits, loader, pulse, snake, wave, chevronLeft, chevronRight, and a vu() helper for generating VU frames from level arrays.
  • A Frame is number[][] indexed as [row][col], with brightness in [0, 1]. Frames smaller than rows x cols are padded with zeros via the internal ensureFrameSize helper.
  • When pattern is set, animation is disabled regardless of frames, autoplay, or loop.
  • mode="vu" takes precedence over both pattern and frames when levels is non-empty.
  • Animation uses a fixed-timestep accumulator on requestAnimationFrame, keeping frame cadence stable under variable paint rates.
  • Cells render as SVG <circle> elements with a gradient fill plus a Gaussian-blur glow filter on active pixels.
  • The container advertises role="img" and uses the provided ariaLabel (falling back to "matrix display"). Because role="img" is treated as an atomic graphic, per-frame updates are not re-announced — supply an ariaLabel that describes the overall display rather than per-frame content.