LingSys
↓ 0 downloadsYour machine's memory, GPU, motherboard and network, drawn and played in the Ling design system: LingSys, a small build for USB liquid-cooler displays, LingNet for IPv4/IPv6, DNS and packets, and a LingOS edition. 64-bit and 32-bit Windows builds included.
Keywords system-monitor, visualization, network, ipv6, gpu, lcd, lingos, ling
lingfu install LingSysCode / Files (48)
README.md
# HatchSYS (HatchMEM)
Your machine, drawn and played in the Ling design system: a main screen with the system's vital signs, and a menu that opens four sections, **Memory**, **GPU**, **Board** (motherboard and CPU) and **Network**. Written in Ling (no Rust).
Circles are things coming in (a page read, a packet received, a frame drawn). Squares are things going out (a page written, a packet sent, a copy). Every section has seven channels that take the seven Ling hues, and the same numbers drive an additive synth.
## Run
```
.\run.ps1 live data from this machine (starts four samplers, stops them on exit)
.\run.ps1 sim no samplers; a made-up machine (16 GB, four GPUs, 16 cores)
.\run.ps1 full borderless fullscreen
.\run.ps1 memonly the original single-screen HatchMEM (hatchmem.ling)
```
or `ling run --interp hatchsys.ling [sim] [full] [shot]` by hand, in this folder, after starting the samplers yourself.
## Executables, 64-bit and 32-bit
```
python tools/build_exe.py LingSys, LingSysMini and LingNet for 64-bit (dist\x64) and 32-bit (dist\x86) Windows
python tools/build_exe.py net --x86 one app, one architecture
python tools/make_icons.py redraw the icons (assets\*.ico) before building
```
The built programs are also in `releases/` (with `SHA256SUMS.txt`): `windows-x64`, `windows-x86` and `lingos`. Double-click an exe, or run `LingSysMini.exe 240x320`, `LingSys.exe full`, `LingNet.exe sim`. Each is self-contained (14 to 17 MB): it carries the samplers, writes them to `%LOCALAPPDATA%\LingSys`, starts them hidden, and stops them when you close it. If the exe is killed instead of closed, the samplers notice within a second or two and exit on their own. Nothing else needs to sit next to it. Each exe has its own icon: the rings for LingSys and LingSysMini, a constellation for LingNet (`icon.svg`, `icon_net.svg`).
The exes run on the interpreter, not on `ling build`'s default JIT, because the JIT's memory leak (see below) would fill your RAM within minutes; that is why `tools/build_exe.py` exists instead of a plain `ling build`. The first build of each architecture compiles Ling itself and takes about six minutes, later runs about two. The exes are unsigned and start hidden PowerShell windows, so Windows SmartScreen or an antivirus may query them.
**32-bit.** Ling builds for 32-bit Windows with two workarounds, both done by the build script and neither touching the Ling source: one lint is allowed (a window-style constant that wraps to exactly the intended 32-bit value), and the finished exe has its import of `SetWindowLongPtrW` renamed to `SetWindowLongW`. The first is a literal rustc rejects on 32-bit; the second is because `user32.dll` on 32-bit Windows has no export with the `Ptr` name (it is a header macro there), so an unpatched exe stops in the Windows loader with an "entry point not found" error. The proper fix is in Ling's `src/runtime/mod.rs`. I ran the 32-bit exes on 64-bit Windows (window, samplers, live data, flat memory); I have not run them on a 32-bit installation.
## LingOS
```
ling build lingos/hatchsys_os.ling --platform lingos --out dist/lingos -> dist/lingos/apps/hatchsys_os.elf
```
The monitoring programs cannot run on LingOS: its userland (`crates/ling-user`) has print, math, lists, strings, a framebuffer (`plot`, `plot_disc`, `clear`), key polling and a clock, and no windows, files, performance counters or network stack. (Building `hatchmini.ling` for LingOS compiles to native code and then fails to link on `ling_list_set`, and would have no way to draw or read anything anyway.) `lingos/hatchsys_os.ling` is a small LingOS-native edition instead: the Rings view, seven hue rings with crosshatched arcs turning against each other, drawn straight to the framebuffer and moved by a synthetic activity signal. Press any key to quit. Where it computes `act`, real counters can go in when LingOS has them. It builds and links to a valid 64-bit x86-64 ELF (37 KB); I have not run it on LingOS.
## Keys and mouse
| Input | Does |
|---|---|
| `0` / `1` `2` `3` `4` | Main / Memory / GPU / Board / Network |
| `Left` `Right` | previous / next section |
| `Tab` or `Space` | next view in the section (on Main: next section) |
| `Up` `Down` | pick the GPU (GPU section, when you have more than one); in the Fleet view you can also click a card |
| `A` | auto-cycle through every view, 28 s each |
| `H` | hide the HUD text (the menu stays) |
| `P` | pluck mode: clicks and ticks instead of hums and chimes |
| `N` | alerts on / off |
| `M` | mute |
| `Esc` | back to Main; on Main, quit |
| `Q` | quit |
| click | the menu along the bottom (sections, then the views of the current section); on Main, click a card to open its section |
`shot` saves one PNG per view (15 of them) into `screenshots/` and quits.
## Small version for a liquid-cooler display
```
.\run.ps1 mini 320x240 (or 240x320, 480x480, any WxH; 320x240 is the default)
```
`hatchmini.ling` is a cut-down build for a small screen: four rings (memory, GPU, CPU, network), four stat rows with the number you actually want to read, a heat reading in the hub, and the same alert banner. No menu and no sound. It uses the same samplers and the same design system. In landscape the rings are on the left and the rows down the right; in portrait the rings are on top with the rows in a 2 x 2 grid.
### On a USB liquid-cooler display
```
.\run.ps1 lcd hatchmini on the display (add rotate=270 or rotate=180 if it is sideways or upside down)
python tools/lcd_sender.py --test send a test card: colour bars and an arrow that should point to the top of the display
```
This drives displays that identify as USB `0416:5302` ("USBDISPLAY", the Thermalright LCD family that TRCC controls). Ling can only open an ordinary window, so the tool, `tools/lcd_sender.py`, mirrors the hatchmini window: it captures the window (even when other windows cover it), turns it, JPEG-encodes it, and sends it over USB about ten times a second. `run.ps1 lcd` starts the samplers, the mini window and the tool together, and stops them all when you close the mini window.
**Close TRCC first.** Only one program can own the display, so `run.ps1 lcd` closes TRCC and its USB helpers, then restarts TRCC when you are done (if it was running). If you use the tool by hand, close TRCC yourself. You need `pip install pyusb libusb-package pillow`.
How it works, so you can check it: the packet format is the one TRCC's own USB helper (`USBLCDNEW`) sends, read out of TRCC's code. The tool does the same handshake, which makes the display reply with a panel code and serial number; then, for every frame, a 20-byte header (`DA DB DC DD`, picture command, width, height, JPEG format, JPEG length) followed by the JPEG, in 512-byte chunks to endpoint 2. Your display answered the handshake as panel code 58, which TRCC treats as a 240 x 320 portrait panel and feeds by turning its 320 x 240 pictures a quarter turn, which is what `--rotate 90` (the default) does.
What I verified and what I could not: the handshake succeeds, the test card and live frames go out without a USB error, and the frames saved from the tool show hatchmini correctly. I cannot see the display, so the orientation (and whether it looks sharp) is for you to confirm; if the picture is turned, change `rotate=`. Only code 58 has been tried; the tool also knows the packet sizes for codes 54, 72, 128 and 129 from TRCC's code, and uses 240 x 320 for anything else. Frame rate is limited by the interpreter and the USB link, so expect roughly 8 to 10 frames a second.
## LingNet: the network in detail
```
.\run.ps1 net (or dist\LingNet.exe; build it with python tools/build_exe.py net)
```
A separate program for the network alone, with IPv4 and IPv6, DNS, packets and addresses, and everything split by **device**, meaning each network adapter on this PC (Wi-Fi, Ethernet, a VPN, the WSL and VirtualBox adapters...). Keys: `Tab` / `Left` / `Right` or `1`-`6` change view, `Up` / `Down` pick the device (ALL, then each adapter), `H` hides the status line, `Q` quits. The bar along the bottom is clickable: views on the left, device chips on the right; on DEVICES you can also click a card. Optional size and flags: `LingNet.exe 1000x600`, `full`, `sim`.
| View | What it shows |
|---|---|
| Devices | One card per adapter: link speed, bytes in and out, packets, multicast, errors, its first IPv4 and IPv6 address with scope, gateways, its connection count split IPv4 / IPv6, its neighbour count, and a minute of history. |
| Flow | The connections of the chosen device in sectors by remote port; circle = IPv4, diamond = IPv6; pulses at your real in and out rates. The busiest processes on the right. |
| Packets | Seven lanes for the chosen device: bytes in and out, packets in and out, multicast in and out, errors and discards. |
| Protocols | Seven rings: IPv4, IPv6, TCP over each, UDP over each, ICMP (both). The hub is IPv6's share of IP traffic. Connection states for each IP version on either side. Windows counts these per protocol, not per adapter, so this view always covers all devices. |
| DNS | Each device's DNS servers with the latency Windows would get from them (probed directly, IPv4 and IPv6 servers alike), whether AAAA records are answered, and the local DNS cache broken down by record type (A, AAAA, CNAME, PTR, SRV, other, negative). |
| Addresses | For each device: every IPv4 and IPv6 address with prefix, scope (global, private, ULA, link-local, CGNAT) and origin (DHCP, router advertisement, static), gateways, DNS servers, MTU, DHCP. On the right, the neighbours the device can see (ARP for IPv4 on the outer ring, NDP for IPv6 on the inner one), coloured reachable, stale or unanswered. |
What "by device" can and cannot mean: Windows keeps traffic counters per adapter, and its connection table names the local address each connection uses, so bytes, packets and connections can be split by adapter. It does not count traffic for the other machines on your network; the neighbour table shows who is there, not how much they send. Protocol counters (IPv4 against IPv6, TCP, UDP, ICMP) are system-wide.
Privacy: your own addresses are shown, since they are yours. Remote addresses, neighbour addresses and MACs, and the names in your DNS cache are never drawn or written as text; the sampler writes only counts, ports, process names and hashes. The DNS probe sends one small query per configured DNS server, every eight passes or so, for `dns.msftncsi.com` (the name Windows itself uses to check connectivity), straight to that server, so your own cache does not answer it and nothing is added to it.
Honest notes: on this machine IPv6 is link-local only, so the global and ULA scopes (and the IPv6 DNS servers) are exercised only by `sim`; I have not seen them with live data. The detailed sampler takes up to several seconds on the passes that refresh addresses, neighbours and DNS, so LingNet only calls its data stale after 20 seconds. The ring views (Protocols, DNS) run at about 8 fps on the interpreter.
## Main
Four Chesilin rings (memory, GPU, CPU, network) around the uptime, and a card for each section with its headline numbers, a usage bar and the last minute as a trace. Each card carries a `LIVE`, `STALE` or `SIM` tag for its data.
## Sections and views
| Section | View | What it shows |
|---|---|---|
| Memory | Rings | One ring per memory category, an arc of crosshatch for its share of RAM. |
| | Tiles | A 32 x 32 page map along a Hilbert curve (each cell is 1/1024 of RAM), with a Daemon tile legend. |
| | Orbits | The 22 biggest processes. Circle = working set, inscribed square = private bytes, orbit speed follows I/O. |
| | Weave | Seven strings at the pitch and loudness the synth uses; every read and write is a note. |
| GPU | Engines | Seven rings: 3D, compute, copy, decode, encode, VRAM, power. The hub is the card's utilisation. |
| | VRAM | A 1024-cell map of the card's memory; the six biggest processes get a hue each, the rest is violet, free cells are dark. Pulses leave the process that did the work. |
| | Dials | Utilisation, temperature, power, core clock; a minute of history under each; memory clock, fan, PCIe link, throttle reason, driver. |
| | Fleet | Every GPU at once, one card each: a utilisation dial, VRAM, power and temperature bars, and a minute of utilisation. The selected GPU has bright corners; click a card to select it. Up to eight GPUs. |
| Board | Layout | A schematic of the board: CPU socket with one square per logical processor, memory slots (the ones your DIMMs sit in are hatched, with size and speed), PCIe slots with your GPUs in them, an M.2 slot, the chipset, SATA ports, rear I/O. Pulses run along the buses. Beside it: board, BIOS, CPU, memory, uptime, interrupt / DPC / context-switch rates, per-disk throughput and busy time. |
| | Cores | Every logical processor as a body on a ring: circle = clock speed, square = load, hue runs round the ring. |
| | Signals | Seven lanes: CPU load, ACPI temperature, interrupts, DPCs, context switches, disk read, disk write, with the last minute of each. |
| Network | Flow | A constellation: each live TCP connection is a dot, in a sector for its remote port (HTTPS, HTTP, DNS, REMOTE, MAIL, HIGH, OTHER). Receive pulses are circles travelling in, sends are squares travelling out. Link facts on the left, the processes holding the most connections on the right. |
| | Links | Seven rings: receive, transmit, packets in and out, TCP, UDP, retransmits. The hub is your gateway latency. |
| | Traces | Seven lanes with the last minute of each. |
Daemon and Chesilin are the design system's own scripts, so labels are not Latin letters. In Memory the category order and hues are the same everywhere:
| Hue | Ling key | Category | Source |
|---|---|---|---|
| red | asm | KERNEL | paged + non-paged pool |
| orange | lit | STANDBY | cached, reclaimable pages |
| yellow | bind | DATA | private working sets of all processes |
| green | ctl | MODIFIED | pages waiting to be written to disk |
| blue | call | MAPPED | in use but not kernel, private or compressed |
| indigo | fn | COMPRESSED | the Memory Compression store |
| violet | mod | FREE | free and zeroed pages |
## Where the numbers come from
Ling has no builtin that reads operating-system or hardware state, so PowerShell samplers write small text files about once a second and the program reads them. They read the same performance counters Task Manager shows. They never touch process memory.
| File | Sampler | Reads |
|---|---|---|
| `mem.snap` | `sampler.ps1` | memory performance counters |
| `gpu.snap` | `sampler_hw.ps1 -Kind gpu` | `nvidia-smi` for the card (temperature, fan, power, clocks, utilisation, VRAM, PCIe link, throttle), Windows GPU Engine / GPU Process Memory counters for engines and per-process VRAM |
| `board.snap` | `sampler_hw.ps1 -Kind board` | SMBIOS (board, BIOS, memory modules, disks), per-processor counters (load, clock, interrupts, DPCs), system counters, disk counters, the ACPI thermal zone |
| `net.snap` | `sampler_hw.ps1 -Kind net` | interface and TCP/UDP counters, `netstat -ano`, and one latency probe |
| `netx.snap` | `sampler_hw.ps1 -Kind netx` | LingNet only: per-adapter counters, IPv4 and IPv6 protocol counters, addresses, gateways, DNS servers and cache counts, neighbour tables, connections by local adapter, direct DNS probes |
The file formats are documented at the top of each sampler. If a sampler stops, its section shows `STALE` after eight seconds and freezes on the last reading. If a sampler has produced nothing (no `nvidia-smi` and no GPU counters, say), that section shows made-up data tagged `SIM`.
### Several GPUs
Engines, VRAM and Dials show one GPU at a time, the selected one (`Up` / `Down`, or a click in Fleet; the HUD says `GPU 1 OF 2`). Fleet shows all of them. The main screen's GPU card lists every card, and the Board layout puts the first three in PCIe slots. Alerts watch every GPU, not just the selected one. NVIDIA cards come first; any other adapter that is really in use (more than 64 MB of VRAM, such as an Intel or AMD card) gets a card of its own with engines and VRAM from the Windows counters, but no temperature, fan, power or clocks.
### Things to know
- **No CPU package temperature, fan speeds or voltages.** Windows does not expose them without a kernel driver. The Board views show the ACPI thermal zone, which on many desktops is a fixed, board-level value. If [LibreHardwareMonitor](https://github.com/LibreHardwareMonitor/LibreHardwareMonitor) or OpenHardwareMonitor is running, the sampler also reads its WMI sensors and the Layout view lists the first five. That path is written but I could not test it here (neither is installed).
- **GPU matching.** Windows names GPUs by LUID, `nvidia-smi` by index. The sampler pairs them by comparing VRAM in use, which is right when your cards use clearly different amounts and can swap engine and process numbers between two cards that use almost the same. Per-process VRAM can add up to more than the card reports; the VRAM view scales it down to fit. Only NVIDIA cards get temperature, fan, power and clocks; other cards still get engines and VRAM from the Windows counters.
- **Gateway latency.** Many routers ignore ping, so when ICMP gets no answer the sampler times a TCP connect (accepted or refused) to the gateway instead. Nothing leaves your network.
- **Remote addresses are never drawn as text.** A connection's place in the constellation comes from a hash of its address and its remote port. `netstat` gives no per-connection traffic, so which dot a pulse leaves is random; the number and speed of pulses follow your real receive and send rates.
- **Memory leak on the JIT.** In Ling 2030.1.48 the default JIT backend never frees heap values (list literals, `list_push`, string building, indexing into nested lists), so any program that builds lists every frame grows without limit: this one, and the original HatchMEM, by about 3 GB a minute, until the machine runs out of memory (it took about 15 minutes to reach 38 GB). Pure arithmetic does not leak. The tree-walking interpreter (`--interp`) frees memory correctly and stays flat, at roughly 20 fps instead of 60, so `run.ps1` uses it by default and `jit` is opt-in for short sessions. If you run `ling run` yourself, pass `--interp`.
- Windows only for live data. `sim` works anywhere Ling does.
- The page map and the board layout are drawn from real totals and your real hardware inventory; they do not show physical addresses or the actual trace routing of your board. Which memory slots are hatched assumes the usual two-DIMM convention (A2 and B2 first).
## What moves
- Rotation of every ring and map follows the current section's activity; hue drifts with the outgoing side (up) and incoming side (down); saturation rises with how full the section is (RAM used, VRAM used, CPU load, link use).
- A channel's lightness and hatch density rise with its recent activity.
- Pulses are placed by channel weights and, where it makes sense, by body: the process that did the I/O (Memory, GPU), the core that was busy (Board), the connection (Network). Allocations are circles in the section's allocation channel (FREE, VRAM, CHIPSET, TCP).
## Sound
32 `audio_tone` slots: 7 channels x 4 harmonics (28), plus 4 shimmer partials that open with the section's activity. The root rises from 55 Hz toward 81 Hz as the section fills. Each channel sits on a fixed ratio above it (1, 3/2, 2, 9/4, 3, 15/4, 9/2), loudness follows its share, LFO speed and depth follow its activity, and each is placed round the listener. Every pulse also fires a short chime (sine for in, triangle for out), at most 3 per frame and 14 per second. **Pluck mode** (`P`) swaps that for something percussive: the hum is switched off, and each pulse is a noise click on the attack followed by a short plucked note with almost no release (about 0.11 s triangle for an in pulse, 0.045 s square for an out pulse, an octave up), up to 5 per frame. The sound follows whichever section you are in; Main plays at half level.
## Alerts
When the machine "sounds" irregular, an alarm plays in whichever section you are in and a flashing banner names the problem along the top. It checks all four sections all the time, and starts 12 s after launch so the running averages can settle. Each kind of alert fires at most once every 25 s. `N` turns alerts off; `M` mutes the alarm but keeps the banner.
| Alert | Fires when |
|---|---|
| MEMORY NEARLY FULL | RAM in use (everything but free and standby) reaches 93% |
| GPU RUNNING HOT | NVIDIA reports thermal slowdown, or 88 C or more |
| GPU MEMORY FULL | VRAM reaches 97% |
| CPU ZONE HOT | the ACPI thermal zone reaches 85 C |
| SAMPLER STALE | any sampler has stopped updating |
| GPU / CPU LOAD SPIKE | load jumps more than 50 points above its recent average |
| LATENCY SPIKE | gateway latency exceeds 3 x its average plus 150 ms |
| PACKET RETRANSMITS | TCP retransmits climb past roughly 700 a second |
The first five are serious: four square-wave beeps, high. Spikes and retransmits are three softer triangle beeps. The thresholds are at the top of `alert_check` in `src/86_alert.ling`. Alerts also run on `sim` data, which is made up.
## Files
- `hatchnet.ling`: LingNet, also **generated** (the shared parts plus `src/66_netx.ling` and `src/97_netmain.ling`).
- `hatchmini.ling`: the small LCD build, also **generated** (by `tools/build.py`, from the same `src/` parts plus `src/95_mini.ling`).
- `hatchsys.ling`: the whole program, **generated**. One file on purpose: the Ling JIT does not see top-level bindings of `use`d modules.
- `src/*.ling`: the parts it is built from (head and constants, helpers, snapshot parsing and simulation, Memory, audio, shared rings and scope, GPU, Board, Network, Main, menu and HUD, drive, alerts, main loop). Edit these, then run `python tools/build.py`.
- `hatchmem.ling`: the original single-screen memory program, unchanged. It is also where the glyph block lives.
- `sampler.ps1`, `sampler_hw.ps1`: the samplers.
- `tools/gen_glyphs.py`: rewrites the glyph block in `hatchmem.ling` from `../ling_hatch/ling_bundle.js`. Run it if the bundle changes, then run `tools/build.py`.
- `tools/build_exe.py`: builds `dist/LingSys.exe` and `dist/LingSysMini.exe` (needs the Ling source tree that `ling` was built from, and Rust).
- `lingos/hatchsys_os.ling`: the LingOS edition (see LingOS above).
- `assets/`, `icon.svg`, `icon_net.svg`, `tools/make_icons.py`: the icons and the script that draws them.
- `releases/`: the built programs for 64-bit and 32-bit Windows and LingOS, with checksums.
- `tools/lcd_sender.py`: mirrors a window to a USB liquid-cooler display (see above).
- `tools/build.py`: concatenates `src/` and the glyph block into `hatchsys.ling`.
- `run.ps1`: launcher.
## Notes
- Tested here with Ling 2030.1.48 (JIT) at 1280x720 on a machine with two RTX 4090s, a 32-thread Intel CPU and Wi-Fi: about 60 fps in every view, with live and with `sim` data. I checked all 14 views by screenshot, the GPU section with each card selected, and the menu and card hit-testing headless. I have not driven the keyboard or mouse by hand, listened to the sound path, or tried `full`. `audio_fx_reverb` and `audio_fx_delay` are called, but Ling may treat them as no-ops.
- Ling reserves keywords from many human languages, so short variable names can collide (`si`, Spanish for "if", broke the parser once). If a build fails with `unexpected token` and no line, bisect by running the `src/` parts one at a time.
- The samplers cost a little CPU: the GPU and network ones take roughly a second and a half per pass, the board one under a second.