Reading the screen
Panels
One rule runs through every panel: a reading the Mac does not give is left out. No 0 W for a rail that did not publish, no 0 rpm for a fan that does not exist, no 0% battery on a desktop. When a panel is waiting for its first reading its numbers show ….
Numbers live in the panel title; the body is the graph. enter or e on a focused panel expands it.
cpu#
╭ cpu 3% S 1.3G · P 1.0G 1.3W 44° ──────────── 10% [↗]╮The title shows the load, each cluster’s clock (on Glance, or when there is room), busy when it disagrees with the load, CPU watts, the CPU temperature, and the thermal state when it is not nominal. As the window narrows, the least important tokens drop out whole. The example above is from plottypus --demo at idle.
Scaled load vs busy#
The headline percentage is scaled: each core’s Mach busy share multiplied by the ratio of the clock it actually ran at (from IOReport DVFS residency) to its top clock. A core that is 100% busy at 600 MHz on a 4.3 GHz chip counts as about 14%. That is how much of the chip’s real capacity is in use.
busy is the plain non-idle share across all cores, the number Activity Monitor and top give. plottypus shows it next to the headline only when the two differ by more than 10 points. At idle on Apple Silicon they differ a lot, because the cores are awake but clocked down.
That is why the cpu graph often reads lower than Activity Monitor. Neither is wrong; they answer different questions. Where IOReport residency is not available, scaled equals busy.
Clusters and clocks#
Apple Silicon groups cores into clusters: Efficiency (E) and Performance (P) on M1 to M4, Super (S) and Performance (P) on M5 Pro and Max. S 1.3G · P 1.0G in the title is each cluster’s clock.
The expanded view has:
- the chip, core counts, GPU cores and memory in a left column (
M5 Pro,6S+12P,20-core GPU,24 GB); - one load graph per cluster, titled with its load and its clock (
super 3% 1.3G); - one temperature graph per cluster zone (
super zone 43°,perf zone 41°); - one bar per logical core, labelled by cluster (
S0…S5,P0…P11,E0…), in Mach order.
A cluster’s MHz is the residency-weighted average of the DVFS states it spent the last window in. It is absent until the first window lands (about a second after start), and on chips whose clock tables are not known yet.
Zone temperatures come from SMC sensors mapped per chip into Super, Performance, Efficiency and GPU zones. They are labelled as zones, never as per-core die temperatures.
When any throttle channel reads, the expanded cpu view adds a line such as throttled: power cap 25%, cpu cut 10%.
The per-core bars can be turned off with the panels.cores setting.
gpu#
╭ gpu 2% 466MHz 0.4W 38° ──────────── 10% [↗]╮GPU load, clock, watts and temperature. Load and clock come from IOReport; GPU watts come from the IOReport GPU Energy channel, which publishes on every read, so they stay live on every chip and every macOS version seen so far. Like the cpu, a busy token appears when the plain busy share and the scaled load differ by more than 10 points.
plottypus also reads GPU memory in use and the GPU core count (shown with the chip identity in the expanded cpu view). The GPU box hides itself on a Mac that reports no GPU, and panels.gpu = false hides it by choice.
mem#
╭ mem 12.8G/24.0G swap 820M ─────[↗]╮
│████████████████▒▒▒▒▒▒▒▒────────────│Used is the Activity Monitor figure: active, inactive, wired, speculative and compressor pages, minus purgeable and file-backed pages. The bar under the title is the composition, one stacked bar: app, wired and compressed in solid ink, cached files shaded (▒), the rest free.
When memory pressure leaves nominal, the title adds it in colour: ● warn or ● critical. At nominal it prints nothing.
Expanded, the view shows:
- the used graph, titled with the kernel’s free level:
memory 12.8G/24.0G · 47% free. That percentage iskern.memorystatus_level, the same numbermemory_pressureprints; - the composition with a legend:
app 8.5G wired 3.1G compressed 1.2G cached 6.4G. Cached is file-backed plus purgeable memory, Activity Monitor’s “Cached Files”; - swap used out of total, and swap-in and swap-out rates.
Per-process memory is a different number (footprint). See Processes.
heat#
╭ heat peak 47° gpu 38° fans idle ──── 35–50° [↗]╮The hottest reading, the GPU, and the fans. Temperatures come from the Mac’s full SMC key list (3,626 keys on an M5 Pro, 285 of them temperatures) plus the IOKit HID temperature services, read on their own thread.
- Fans show their rpm and graph when they spin. When every fan is stopped the title says
fans idle; Apple Silicon Macs often run with fans off. - Fanless MacBook Airs have no fans to show. The heat panel keeps its temperatures and leaves fans out.
- Macs that expose no temperature sensors to user space hide the heat panel entirely.
Expanded, heat shows a graph per zone (super, perf, efficiency), the package, the GPU die, one graph per fan, and a line of other readings such as the SSD (nand 36°). The temperature scale in the border is a band (35–50°), not 0 to 100.
Colour on temperatures is fixed to degrees, so 45°, 70°, 80°, 88° and 95° always look the same in every title.
Activity Monitor does not show any of this. The short version, with the JSON fields: Check Mac CPU temperature in the terminal.
power and battery#
╭ power 7.2W cpu 1.3W gpu 0.4W dram 0.6W 79% ▼ 7.2W 7h53m ──── 10W [↗]╮The first number is whole-system power: the SMC PSTR key, what the Mac draws from its adapter or battery. Then come the rails that read: cpu, gpu, ane (Neural Engine) and dram. A rail that has no reading is left out, not printed as 0.0W. Anything under 0.05 W counts as absent.
Where CPU watts come from:
- M5 Pro (Mac17,8): the SMC CPU power rails, averaged over three sensor sweeps. They were calibrated against IOReport’s CPU energy on that Mac.
- Every other Mac: the IOReport
CPU Energycounter.
≈ and held readings#
On macOS 27 the IOReport counters for CPU, ANE and DRAM publish in batches, seconds to minutes apart. plottypus computes each rate from the driver’s own timestamps between two publications. Between batches it holds the last value:
- a held or estimated value is drawn dim with
≈in front:≈1.3W; - the expanded view says how old it is:
held 4s; - after 10 seconds it is dropped and the rail disappears until the next batch.
So a rail that blinks in and out is the Mac publishing slowly. It is not plottypus losing data, and it never becomes a fake zero. ANE and DRAM need two publications before their first value, which can take up to a couple of minutes after launch. The how-to, including the jq one-liner, is Mac CPU watts in the terminal, no sudo.
Battery#
On a laptop the title adds the battery:
| Reads | Means |
|---|---|
79% ▼ 7.2W 7h53m |
discharging at 7.2 W, about 7 h 53 min left |
62% ▲ 30W |
charging at 30 W |
80% held |
on the charger, not charging, on purpose |
100% full |
charged |
The expanded view has a system-watts graph, a battery-charge graph (zoomed to its recent range, so a slow drain has a visible slope), a flow graph for watts in and out, and a list of facts. Each one appears only when macOS reports it:
- time left on battery, or time until full;
- health and cycles:
health 95%, 214 of 1000 cycles; - the adapter:
adapter 96W USB-C; - Low Power Mode, when it is on;
- what is keeping the Mac awake:
kept awake by caffeinate; - why the charge is held.
held at the charge limit to spare the batterywhen macOS stops at its charge limit. Any other reason code shows asmacOS is holding the charge (reason 0x…); plottypus prints the raw code rather than guess at a meaning it has not decoded.
Desktops have no battery. There the power panel shows system watts and its rails only, and the footer has no battery reading. A desktop that reports no power at all hides the panel.
Per-process energy is in the process table: the power column appears while you are on battery. See Processes.
net#
╭ net en0 ↓186KB/s ↑16KB/s ──── 500KB/s [↗]╮Download and upload on one interface: the one carrying the default route (a VPN’s utunN counts), or, with no default route, the busiest physical enN. Rates come from the exact 64-bit ifmib counters, so they do not wrap on a fast link.
The graph is mirrored: download grows up from the middle, upload down, on one shared scale. Speeds are in bytes per second by default, like Finder; set units.net_bits = true (settings → general → network units) for bits per second, like your ISP.
panels.net turns the panel off. Expanded, net shows separate down and up graphs and hops to disk.
disk#
Off by default; turn it on with panels.disk = true or the sre preset (4).
The title shows the main volume’s used and total space; the graph is read and write throughput in bytes per second, mirrored like the network graph. Expanded, disk lists every volume with its used and total space.