SystemMonitor
A live top/htop pane whose meters and columns wander on seamless harmonics — a long, non-repeating cycle that reads as a running machine, not a looping GIF.
Install
$ npx shadcn@latest add wisp.pouriah.com/r/system-monitor.jsonLive · DOMlive state
wisp@forge:~$htop11:00:00
CPU14.8%
Mem64.1%
Swp9.6%
Tasks 5, 0 running · Load 1.20 1.26 1.15 · Up 3 days, 04:12
PIDUSERCPU%MEM%COMMAND
1287wisp39.86.1next dev --turbo --port 3000
990wisp45.93.7node scripts/build-registry.ts
1501wisp10.69.6chromium --headless --disable-gpu --remote-debugging-port=9222
655wisp14.61.7esbuild --watch --bundle --format=esm
1042root7.62.1/usr/bin/dockerd -H fd:// --containerd=/run/...
wisp@forge:~$
"use client";
import * as React from "react";
import { cx, useScriptedSequence } from "@pouriahlabs/wisp-ui";
/**
* SystemMonitor — a live `top`/`htop` pane that never repeats on you.
*
* The other demo surfaces reveal a fixed script and loop it; on a landing page
* a short loop announces itself, and a monitor that plays the same eight seconds
* on repeat reads as a GIF, not a running machine. So this one doesn't reveal —
* it *runs*. Every meter and every CPU/MEM column is a sum of sine harmonics
* whose periods each divide the cycle length, which buys two things at once: the
* frame at the end of the cycle is bit-identical to the frame at the start, so
* the loop seam is invisible, and with a long default cycle (~86s) a reader
* watching the mockup never reaches it anyway. The numbers wander, the bars
* breathe, and nothing ever snaps back.
*
* Structural rule shared with the rest of the tier: every row is laid out up
* front at a fixed height and only its *values* mutate — the process list is
* sorted by PID and never reorders, so the pane can't reflow or push the page
* around while it plays. Bars tween via `transform: scaleX` on the compositor
* between ticks, never `width`, so growth never triggers layout.
*
* The lifecycle comes free from `useScriptedSequence`, used here as a frame
* clock rather than a line revealer: it pauses off-screen and in hidden tabs,
* and under reduced motion it rests on one composed, fully-populated frame —
* never an empty box.
*
* Colour comes from custom properties so the component carries no palette of
* its own; see `.wisp-mon` in `wisp-ui/styles.css` for the defaults.
*
* Every number here is synthetic and never settles — there is no "final
* value" the way `NumberTicker` has one, so there's nothing meaningful to
* read out digit by digit. `role="img"` with one static `aria-label`
* describes what the mockup shows instead of exposing dozens of numbers that
* change every tick and say nothing.
*/
export interface MonitorProcess {
pid: number;
user: string;
command: string;
/** Baseline CPU%, 0–100. */
cpu?: number;
/** Baseline MEM%, 0–100. */
mem?: number;
/** How far CPU% swings around its baseline. */
swing?: number;
}
export interface SystemMonitorProps extends React.HTMLAttributes<HTMLDivElement> {
/** The process table. Defaults to a realistic dev-box deck. */
processes?: readonly MonitorProcess[];
/** Cores the aggregate CPU meter divides by. */
cores?: number;
/** Frames in one seamless cycle. Larger = longer before it repeats. */
ticks?: number;
/** Milliseconds per frame. */
tickMs?: number;
/** Pause before the cycle restarts. The cycle is seamless, so keep it small. */
loop?: number;
/** Fixed uptime string shown in the status line. */
uptime?: string;
/** `user@host` for the prompt line above the meters. `""` drops the prompt. */
host?: string;
/** What that prompt says it ran. */
command?: string;
}
const DEFAULT_PROCESSES: readonly MonitorProcess[] = [
{ pid: 1287, user: "wisp", command: "next dev --turbo", cpu: 41, mem: 6.2, swing: 34 },
{ pid: 990, user: "wisp", command: "node scripts/build-registry", cpu: 27, mem: 3.4, swing: 31 },
{
pid: 1501,
user: "wisp",
command: "chromium --headless --renderer",
cpu: 23,
mem: 8.9,
swing: 27,
},
{ pid: 655, user: "wisp", command: "esbuild --watch --bundle", cpu: 17, mem: 1.9, swing: 22 },
{ pid: 1042, user: "root", command: "/usr/bin/dockerd", cpu: 11, mem: 2.3, swing: 15 },
{ pid: 812, user: "postgres", command: "postgres: writer process", cpu: 4, mem: 3.8, swing: 9 },
{ pid: 33, user: "root", command: "kworker/u16:2-events_unbound", cpu: 6, mem: 0.2, swing: 13 },
{ pid: 214, user: "root", command: "/lib/systemd/systemd-journald", cpu: 2, mem: 0.6, swing: 6 },
];
function clamp(v: number, lo: number, hi: number): number {
return v < lo ? lo : v > hi ? hi : v;
}
/** Deterministic 0–1 from an integer seed — used for per-row phases. */
function hash(n: number): number {
const x = Math.sin(n * 12.9898) * 43758.5453;
return x - Math.floor(x);
}
/**
* A seamless wander in [-1, 1]. Every harmonic completes a whole number of
* cycles over `ticks`, so value(t + ticks) === value(t): no loop seam.
*/
function wander(
t: number,
ticks: number,
harmonics: ReadonlyArray<[k: number, amp: number, phase: number]>,
): number {
let v = 0;
for (const [k, amp, phase] of harmonics) {
v += amp * Math.sin((2 * Math.PI * k * t) / ticks + phase);
}
return v;
}
function cpuOf(proc: MonitorProcess, seed: number, t: number, ticks: number): number {
const base = proc.cpu ?? 8;
const swing = proc.swing ?? 18;
const p1 = hash(seed) * Math.PI * 2;
const p2 = hash(seed + 7) * Math.PI * 2;
const k1 = 1 + Math.floor(hash(seed + 1) * 3); // 1–3 whole cycles
const k2 = 4 + Math.floor(hash(seed + 2) * 4); // 4–7 whole cycles
const w =
0.62 * Math.sin((2 * Math.PI * k1 * t) / ticks + p1) +
0.38 * Math.sin((2 * Math.PI * k2 * t) / ticks + p2);
return clamp(base + swing * w, 0, 99.9);
}
function memOf(proc: MonitorProcess, seed: number, t: number, ticks: number): number {
// Resident memory drifts far less than CPU — a small, slow breath.
const base = proc.mem ?? 1;
const w = wander(t, ticks, [[1, 0.7, hash(seed + 3) * Math.PI * 2]]);
return clamp(base + base * 0.14 * w, 0, 99.9);
}
// Module scope, not nested in SystemMonitor: a component literal declared
// inside a render function is a new type every render, so React would
// unmount/remount this node on every tick instead of updating it — killing
// the fill's transition before it ever gets to play.
const Meter = ({ label, pct }: { label: string; pct: number }) => (
<div className="wisp-mon-meter">
<span className="wisp-mon-key">{label}</span>
<span className={cx("wisp-mon-bar", pct >= 82 ? "is-hot" : pct >= 58 && "is-warm")}>
<span className="wisp-mon-bar-fill" style={{ transform: `scaleX(${pct / 100})` }} />
</span>
<span className="wisp-mon-pct">{pct.toFixed(1)}%</span>
</div>
);
export function SystemMonitor({
processes = DEFAULT_PROCESSES,
cores = 8,
ticks = 240,
tickMs = 360,
loop = 90,
uptime = "3 days, 04:12",
host = "wisp@forge",
command = "htop --sort-key PERCENT_CPU",
className,
...props
}: SystemMonitorProps) {
const ref = React.useRef<HTMLDivElement>(null);
const timings = React.useMemo(
() => Array.from({ length: Math.max(1, ticks) }, () => ({ hold: tickMs })),
[ticks, tickMs],
);
const shown = useScriptedSequence(ref, { steps: timings, loop });
// `shown` is our frame clock: it climbs 0…ticks and wraps. Because the state
// is periodic with period `ticks`, the settle frame (`shown === ticks`) folds
// onto frame 0 — a normal, fully-populated frame, never a blank one.
const frame = shown % ticks;
// ---- meters ----------------------------------------------------------
const cpuLoad = (t: number) =>
clamp(
processes.reduce((sum, p, i) => sum + cpuOf(p, p.pid + i, t, ticks), 0) / Math.max(1, cores),
2,
99,
);
const cpuPct = cpuLoad(frame);
const memPct = clamp(
52 +
22 *
wander(frame, ticks, [
[1, 0.66, 0.4],
[3, 0.34, 2.1],
]),
8,
96,
);
const swpPct = clamp(
7 +
6 *
wander(frame, ticks, [
[2, 0.7, 1.2],
[5, 0.3, 4.0],
]),
0,
60,
);
// Load average: three lagged reads of the CPU meter, so the trio moves like
// the real thing instead of three unrelated dials.
const at = (lag: number) => (frame - lag + ticks) % ticks;
const la1 = ((cpuLoad(at(2)) / 100) * cores).toFixed(2);
const la5 = ((cpuLoad(at(18)) / 100) * cores).toFixed(2);
const la15 = ((cpuLoad(at(48)) / 100) * cores).toFixed(2);
// ---- process rows ----------------------------------------------------
const rows = processes.map((p, i) => {
const cpu = cpuOf(p, p.pid + i, frame, ticks);
const mem = memOf(p, p.pid + i, frame, ticks);
const running = cpu > 55;
return { p, cpu, mem, running };
});
const runningCount = rows.filter((r) => r.running).length;
// A wall clock that belongs to the cycle rather than to the reader's machine:
// it has to be identical on the server and on the first client frame, and it
// has to return to where it started when the cycle wraps.
const second = Math.floor((frame / ticks) * 3600);
const clock = [11, Math.floor(second / 60) % 60, second % 60]
.map((n) => String(n).padStart(2, "0"))
.join(":");
return (
<div
ref={ref}
role="img"
aria-label="Simulated system monitor showing CPU, memory and process activity wandering over time"
className={cx("wisp-mon", className)}
{...props}
>
{host ? (
<div className="wisp-mon-prompt">
<span className="wisp-mon-host">{host}</span>
<span className="wisp-mon-path">:~$</span>
<span className="wisp-mon-cmd-run">{command}</span>
<span className="wisp-mon-clock">{clock}</span>
</div>
) : null}
<div className="wisp-mon-gauges">
<Meter label="CPU" pct={cpuPct} />
<Meter label="Mem" pct={memPct} />
<Meter label="Swp" pct={swpPct} />
</div>
<div className="wisp-mon-status">
Tasks <b>{processes.length}</b>, <b>{runningCount}</b> running · Load{" "}
<b>
{la1} {la5} {la15}
</b>{" "}
· Up <b>{uptime}</b>
</div>
<div className="wisp-mon-table">
<div className="wisp-mon-head">
<span className="wisp-mon-num">PID</span>
<span>USER</span>
<span className="wisp-mon-num">CPU%</span>
<span className="wisp-mon-num">MEM%</span>
<span>COMMAND</span>
</div>
{rows.map(({ p, cpu, mem, running }) => (
<div key={p.pid} className={cx("wisp-mon-row", running && "is-active")}>
<span className="wisp-mon-num">{p.pid}</span>
<span className="wisp-mon-user">{p.user}</span>
<span className={cx("wisp-mon-num", cpu >= 55 && "is-hot")}>{cpu.toFixed(1)}</span>
<span className="wisp-mon-num">{mem.toFixed(1)}</span>
<span className="wisp-mon-cmd">{p.command}</span>
</div>
))}
</div>
{/* The line the reader would type next. It is what separates a pane that
is running from a screenshot of one — the table can be still for a
second, and this never is. */}
<div className="wisp-mon-tail">
<span className="wisp-mon-host">{host || "wisp@forge"}</span>
<span className="wisp-mon-path">:~$</span>
<span className="wisp-caret wisp-mon-caret" aria-hidden />
</div>
</div>
);
}
Certified against the contract
- ✓Reduced motion paints one composed still frame — never a blank box
- ✓Pauses off-screen and in hidden tabs
- ✓Re-reads design tokens when the theme changes
- ✓SSR-safe: no hydration mismatch, no layout shift
- ✓Decorative layers are aria-hidden and pointer-events-none
- ✓Device pixel ratio clamped
- ✓Zero runtime dependencies beyond React