npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glowbox/react

v1.12.0

Published

glowbox components for React — <LedGrid> (3D WebGL LED grid), <NixieTube>, <SevenSegment>, <FlipDots>, <SplitFlap>, <NeonSign>, <VfdPanel> and <LcdModule>, over the sibling display cores.

Readme

@glowbox/react

glowbox components for React — one per display core: <LedGrid> (the 3D WebGL LED grid), <NixieTube>, <SevenSegment>, <FlipDots>, <SplitFlap>, <NeonSign>, <VfdPanel> and <LcdModule>, each a thin live-updating wrapper over its sibling core package.

⚡ Open in StackBlitz — a running <LedGrid> + <NixieTube> playground in your browser, no install.

yarn add @glowbox/react
# peer: react ^18 || ^19   (the eight display cores come along as dependencies)

<LedGrid>

import { LedGrid, type LedDisplay } from '@glowbox/react';

const draw = (d: LedDisplay, dt: number) => {
	d.clear();
	d.sphere([4, 4, 4], 3, '#00aaff');
};

export default function App() {
	return (
		<div style={{ width: 480, height: 480 }}>
			<LedGrid
				size={[8, 8, 8]}
				draw={draw}
				led={{ glow: 3, offColor: '#0a0a12' }}
				camera={{ autoOrbit: true, projection: 'perspective' }}
				color={{ background: '#000', gain: 1.1 }}
				interaction={{ zoom: true }}
			/>
		</div>
	);
}

| prop | type | notes | | --------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | size | [number, number, number] | grid dims [nx, ny, nz] (changing it resizes in place — no remount) | | draw | (d: LedDisplay, dt: number)=>void | called every frame (dt in seconds); write voxels here | | led | LedOptions | style shape stagger rgb rgbLayout vivid outline outlineColor size offSize glow offColor | | color | ColorOptions | background gain tint | | camera | CameraOptions | yaw pitch distance fov projection autoOrbit orbitSpeed pitchLimits (autoOrbit defaults off under prefers-reduced-motion) | | interaction | InteractionOptions | drag dragSpeed zoom zoomLimits | | quality | QualityOptions | pixelRatio antialias paused fps (frame-rate cap) alpha (transparent canvas; fixed at creation) | | label | string | accessible name for the canvas (aria-label; default 'LED grid') | | oncreate | (d: LedDisplay \| null)=>void | called with the display on create, null on teardown — the ref flips silently; this is the signal | | className / style | — | forwarded to the <canvas> |

Forward a ref to reach the imperative LedDisplay handle (snapshot(), stats, setCamera, …), or take it from oncreate:

const grid = useRef<LedDisplay | null>(null);
<LedGrid ref={grid} size={[8, 8, 8]} />;
// grid.current?.snapshot()

The grouped props mirror @glowbox/led-grid's options 1:1 and update live — even size resizes the grid in place. draw is bound as one onFrame subscription — onFrame callbacks stack, so subscribing more via the handle layers on top of draw rather than replacing it. See @glowbox/led-grid for defaults, the voxel API, and colour semantics.

<NixieTube>

import { NixieTube } from '@glowbox/react';

<div style={{ width: 80, height: 150 }}>
	<NixieTube value="7" tubeStyle="classic" color="#ff6a12" />
</div>;

| prop | type | notes | | --------------------- | ------------------------------- | ---------------------------------------------------------- | | value | string \| number \| null | the lit symbol: 09, :, -, or null/'' for dark | | tubeStyle | 'classic' \| 'slim' \| 'tall' | physical tube style (maps to the core style option) | | color | Color | glow colour (default warm nixie orange) | | glow | number | glow strength 0..1 | | background | Color | tube glass colour | | mesh | boolean | draw the honeycomb anode mesh (default true) | | ghost | boolean | show the unlit cathode stack (default true) | | pixelRatio | number | cap on devicePixelRatio | | label | string | accessible name (aria-label); defaults to the lit symbol | | oncreate | (t: NixieTube \| null)=>void | the tube on create, null on teardown | | className / style | — | forwarded to the <canvas> (CSS) |

Props update live; forward a ref (or pass oncreate) for the imperative NixieTube handle (setValue, setOptions, resize, snapshot). The core's bare mode (transparent canvas for 3D compositing) is core-only — use createNixieTube directly for that. See @glowbox/nixie for defaults + the size-adaptive rendering.

<SevenSegment>

import { SevenSegment } from '@glowbox/react';

<div style={{ width: 76, height: 130 }}>
	<SevenSegment value="7" displayStyle="vfd" age={0.4} />
</div>;

A seven-segment digit over @glowbox/seven-segment — per-segment cross-fades, ageing, LED/VFD materials. value takes 09, -, hex A b C d E F, : (the clock separator — give it a narrow slot), or null for dark; displayStyle ('led' | 'vfd'), dp, color, background, glow, ghost, age (0..1 — dimming, then flicker, then a dead segment), transition (per-segment fade ms), pixelRatio, label mirror the core options and update live; forward a ref (or pass oncreate) for the imperative SevenSegmentDisplay handle. See @glowbox/seven-segment for defaults, the ageing arc, and the geometry export.

<FlipDots>

import { FlipDots } from '@glowbox/react';

<div style={{ width: 560, height: 280 }}>
	<FlipDots cols={28} rows={14} frame={(x, y) => (x + y) % 2} sound />
</div>;

An electromechanical flip-dot board over @glowbox/flip-dot — physical disc flips, scan-wave stagger, an optional solenoid click. frame takes row-major 0/1 bits (ditherFrame output fits) or an (x, y) => on function — only dots that actually change flip; cols, rows, shape ('disc' | 'square'), onColor, offColor, board, gap, shaded, flipMs, axis, stagger ('scan' | 'random' | 'none'), scanMs, sound (true = 0.5, or a 0..1 volume), pixelRatio, label mirror the core options and update live; a ref or oncreate reaches the imperative FlipDotBoard handle (set, setFrame, dotAt/dotRect hit-testing). See @glowbox/flip-dot for defaults and ditherFrame.

<SplitFlap>

import { SplitFlap } from '@glowbox/react';

<div style={{ width: 640, height: 80 }}>
	<SplitFlap cols={12} text="DEPARTURES" sound />
</div>;

A Solari split-flap board over @glowbox/split-flap — forward-only drum wraps, perspective card falls, a card-slap. text takes a string ('\n' splits rows) or one string per row; cols, rows, charset (plus per-field drums and per-flap palette — see chromaDrum), card, ink, board, gap, font, shaded, flipMs, sound, pixelRatio, label mirror the core options and update live; a ref or oncreate reaches the imperative SplitFlapBoard handle (setLine/setChar, cellAt/cellRect hit-testing). See @glowbox/split-flap for the drum model and the colour drums.

<NeonSign>

import { NeonSign } from '@glowbox/react';

<div style={{ width: 640, height: 240 }}>
	<NeonSign text="OPEN" gas="neon" program="flash" sound />
</div>;

A glass-tube neon sign over @glowbox/neon — single-stroke tube letterforms, electrode strike sequences, visible unlit glass, transformer hum. A text change re-glasses and strikes on; font ('script' | 'sans' | a custom NeonFont), art (sign artwork from SVG path data), color, gas, wall, polarity ('absorb' inks a pale wall — the light-theme neon), on, lineOn, glow, glass, electrode, age, flicker, tired, program ('steady' | 'flash' | 'chase' | 'reveal'), speed, tubes, align, lineSpacing, letterSpacing, tilt, padding, strikeMs, sound, mains, pixelRatio, label mirror the core options and update live; a ref or oncreate reaches the imperative NeonSign handle (power, jolt, sectionAt/sectionRect hit-testing). See @glowbox/neon for the gases, the art pieces, and the wear arc.

<VfdPanel>

import { VfdPanel, type VfdElement } from '@glowbox/react';

const layout: VfdElement[] = [
	{ kind: 'digits', name: 'track', x: 8, y: 8, w: 120, h: 48, chars: 4 },
	{ kind: 'legend', name: 'play', x: 140, y: 8, w: 40, h: 16, text: 'PLAY' }
];

<div style={{ width: 640, height: 128 }}>
	<VfdPanel frame={[320, 64]} layout={layout} values={{ track: 'A-12', play: true }} />
</div>;

A vacuum-fluorescent panel over @glowbox/vfd — declare the hardware once (frame + layout: digits, legends, bars, icons, scales, dot grids, rules) and drive content through values, by element name: a string/number drives a digits field or a scale cursor, a boolean a legend/icon, a number[] a bars element (only changed entries are pushed — the type is exported as VfdValue). The envelope props (phosphor, filter, zones, brightness, persistence, filament, grid, age, glow, bezel, glass, on, selfTest, pixelRatio, label) update live and never re-compile; a layout change goes through setLayout, the one expensive call. A ref or oncreate reaches the imperative VfdPanel handle — animated content (setDots, a spectrum's setBars) wants it. See @glowbox/vfd for the element kinds and the envelope physics.

<LcdModule>

import { LcdModule } from '@glowbox/react';

<div style={{ width: 480, height: 150 }}>
	<LcdModule text={'HELLO\nWORLD'} cursor="block" />
</div>;

A character LCD module (HD44780-class) over @glowbox/lcd — the family's first reflective display: liquid-crystal response smear, the contrast pot, crosstalk streaks, the boot row of solid blocks. text takes a string ('\n' splits rows) or one string per row — the shutters chase it at crystal speed; cols, rows, panel ('green' | 'blue' | 'white'), backlight, contrast, response, ghost, cursor ('none' | 'line' | 'block'), age, on, boot, bezel, pixelRatio, label mirror the core options and update live; a ref or oncreate reaches the imperative LcdModule handle (setGlyph for CGRAM custom characters, setCursor, power, cellAt/cellRect hit-testing). See @glowbox/lcd for the panels and the crystal physics.

Using with @glowbox/crt

There is deliberately no <Crt> component: @glowbox/crt's element mode already composites every descendant canvas, so add it as its own dependency and wrap the stage element imperatively:

import { createCrtScreen } from '@glowbox/crt';
import { NixieTube } from '@glowbox/react';
import { useEffect, useRef } from 'react';

function CrtTube() {
	const stage = useRef<HTMLDivElement>(null);
	useEffect(() => {
		const crt = stage.current ? createCrtScreen(stage.current) : null;
		return () => crt?.dispose();
	}, []);
	return (
		<div ref={stage} style={{ width: 80, height: 150 }}>
			<NixieTube value="7" />
		</div>
	);
}

Sibling packages with the same components: @glowbox/svelte and @glowbox/vue; content helpers in @glowbox/extras. Each component fills its parent; give the parent a size. Live demos: https://eetu.github.io/glowbox/.