Dot Matrix Display
dot-matrix-displayViewersThe other half of the panel from the segment display. A segment readout can say a number; this is what says C:Small Plate on the top line and Mix=99 Volume=99 on the bottom, and it does it the way the hardware does — a fixed field of dots, five columns by seven or eight rows per character, ink moving across dots that never move. Which kind of screen it is comes from the preset, and the two are genuinely different objects: an LED marquee makes light at the dot, so it is round, bright, haloed, on black glass; a backlit character LCD lights the pane and the dots are shadows on it, so the ink is square and dark on yellow-green, with no halo at all and a spill that comes from the backlight rather than from the characters. Text wider than the panel is cut off as a real module cuts it, or scrolls on a clock derived from elapsed time so a dropped frame cannot make it drift. A bitmap prop drives the dots directly for meters, waveforms and boot logos, and a blinking block cursor marks the field being edited. The unlit grid is one tiled pattern and each line of ink is one path, so a 20x2 screen is a handful of nodes rather than 1,600.
Install
Copies the source into your project. Anything it depends on comes with it.
$ npx shadcn add @syntho-ui/dot-matrix-displayPulls in
First time? Add the registry to your components.json once:
{
"registries": {
"@syntho-ui": "https://engine.syntho.app/r/{name}.json"
}
}Usage
Controlled or uncontrolled, whichever suits the surrounding state.
import { DotMatrixDisplay } from "@/components/ui/dot-matrix-display"
import { themeVars } from "@/lib/theme"
// The backlit character module: dark square ink on yellow-green.
<DotMatrixDisplay
preset="lcd-green"
columns={16}
rows={8}
text={["C:Small Plate", "Mix=99 Volume=99"]}
/>
// The marquee: round lit dots on black glass, scrolling.
<DotMatrixDisplay preset="led-red" columns={12} scroll text="NOW PLAYING — SIDE TWO" />
// Straight to the dots, for anything the character generator cannot say.
<DotMatrixDisplay preset="vfd" bitmap={levels} />
// Re-skin every display in a surface from one style block.
<div style={themeVars({ displayInk: "#ff9f1a", displaySpill: "#ff9f1a" })}>
<DotMatrixDisplay columns={12} text="SYNTHO 128" />
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | string[] | — | A line, or one per array entry. Newlines split too. |
| bitmap | boolean[][] | — | Drive the dots directly, [row][column], ignoring the font — meters, waveforms, logos, anything the character generator has no glyph for. |
| columns | number | — | Character cells across — the panel's physical width. Defaults to the widest line. Text past it is cut off unless scroll is set. |
| rows | 7 | 8 | 7 | Dot rows per cell. Seven is the classic 5x7 module and clips descenders exactly as one does; eight draws them. |
| preset | DisplayPresetId | DisplayPreset | "led-red" | Panel skin. The reflective ones (lcd-green, lcd-blue, lcd-grey) turn the screen into a backlit module: square ink, no bloom, spill from the backlight. |
| shape | "square" | "round" | — | The dot itself. Defaults to square on reflective skins and round on emissive ones, which is how the two kinds of screen are actually built. |
| pitch | number | 4 | Dot spacing in pixels, centre to centre — the display's whole scale. |
| fill | number | 0.74 | Dot size as a fraction of the pitch. Under 1 is what leaves the grid visible. |
| scroll | boolean | false | Marquee lines that do not fit. Stops entirely under prefers-reduced-motion; a line that already fits never moves. |
| speed | number | 18 | Marquee speed in dot columns per second. |
| cursor | DotMatrixCursor | — | The block a module leaves where you are typing: { line?, column, style?: "block" | "underline", blink? }. |
| color | string | — | Override the ink colour. The unlit dots are derived from it and follow. |
| ghost | number | — | How visible the unlit dots are, 0..1. Defaults to the skin's. |
| bloom | number | — | Halo around the lit dots, 0..1. Defaults to the skin's — zero on the reflective ones. |
| spill | number | — | How far light escapes the window onto the panel, 0..1. |
| brightness | number | 1 | Overall drive on the ink. |
| label | string | — | Silkscreen caption above the display. |
| — theme | --uikit-display-ink / --uikit-display-spill | — | Not props: the two CSS custom properties (displayInk / displaySpill via themeVars) that re-skin every display under a wrapper, resolved in CSS with no re-render. Precedence is instance color, then these, then the skin. |
| frame | boolean | true | Draw the panel around the pane. |
Interaction
- Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
- Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
- Double-click resets to
defaultValue. - Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
- Proper
sliderandswitchroles with livearia-valuetext, so a screen reader announces the formatted value rather than the raw number. - The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.