Vitals is a macOS system monitor that lives only in the menu bar. The menu bar icon shows a live CPU mini-curve and usage percentage; clicking it opens a dark pixel dashboard with CPU, memory, network, disk, battery and process details. No Dock icon, no main window.
Requires macOS 26.0 or later. Developed with Xcode 27 / macOS 27 SDK.
- The app icon uses a pixel V-shaped pulse, while the menu bar keeps a crisp monochrome V mark; the menu bar icon is a single-color vector that adapts to light and dark appearances
- Four menu bar display modes: graph, number, graph + number, and icon; the curve color tracks load (green / orange / red) and a pause symbol overlays it when sampling is paused
- CPU: total usage, a per-core bar chart, and a dot-matrix history of the last 120 samples; the overview shows segmented per-core bars plus performance / efficiency core counts; the DETAILS entry offers history, load average and a per-core breakdown
- GPU: the overview shows driver-reported usage, history, and memory in use / allocated; details add renderer and tiler usage, with missing fields shown as N/A
- Memory: used / total, a wired-apps-compressed-available segment bar, system memory pressure and swap; expand for file cache, free pages and swap total
- Network: upload and download rates with history, pixel curves distinguishing upload from download, and peak statistics on the details page
- Disk: startup volume capacity bar; the details page lists every readable block device (including disk images) with read / write rates, operation counts and history, and read/write sampling can be turned off in Settings
- System: 1 / 5 / 15 minute load average, logical core count, thermal state, uptime and low power mode
- Battery: the SYSTEM area shows level, hover reveals charging state and estimated time remaining (IOPowerSources); a low power mode marker
- Processes: the details page lists the top 5 processes by CPU with usage and resident memory (sysctl + libproc delta sampling)
- Thermal state: the SYSTEM area reports NORMAL / WARM / HOT / CRITICAL
- Alerts: system notifications for sustained high CPU (30 consecutive seconds at or above 90% by default), disk usage, and thermal state escalation, with a 5 minute cooldown per alert
- Language: follow the system, Simplified Chinese or English, switching the overview, details, settings, tips and notifications together; necessary product names, process names and technical abbreviations stay as-is
- Settings window: menu bar mode, sampling interval (0.5 / 1 / 2 s), CPU / GPU / memory / network / disk / sensor / battery / process toggles, alert thresholds, and launch at login
| Category | API |
|---|---|
| Visual | Canvas pixel glyphs, an isometric host illustration, segmented bars, dot-matrix curves, square-cornered borders and a fixed dark palette |
| Visual | Overview, details and settings share one set of PixelSection / PixelSegments / PixelTrace / pixel toggles and option buttons |
| Visual | Canvas dot-matrix polylines and segmented per-core bars, with SF Symbols for action icons |
| Logic | @Observable (Observation framework) drives all UI; Swift 6 language mode with strict concurrency |
| Logic | MenuBarExtra(.window) with a custom label and popover content; openSettings plus settings window activation |
| Logic | Sampling: mach host_processor_info / host_statistics64, getifaddrs, sysctl (vm.swapusage, KERN_PROC_ALL), proc_pid_rusage, getloadavg, IOKit block device statistics, IOPowerSources, ProcessInfo.thermalState |
| Logic | SMAppService login item, AppIntents (the "Pause or resume Vitals monitoring" shortcut), UserNotifications alerts |
| Logic | Structured-concurrency sampling loop (Task + Task.sleep), Swift Testing unit tests |
xcodegen generate
xcodebuild -project Vitals.xcodeproj -scheme Vitals -configuration Release \
-destination 'platform=macOS,arch=arm64' -derivedDataPath .build/DerivedData build
cp -R .build/DerivedData/Build/Products/Release/Vitals.app /Applications/
open /Applications/Vitals.appTests:
xcodebuild test -project Vitals.xcodeproj -scheme Vitals -destination 'platform=macOS,arch=arm64' \
-derivedDataPath .build/DerivedDataThe app icon is generated by Tools/make_icon.swift (drawn with CoreGraphics, written to the asset catalog and then scaled to the full set of sizes with sips):
swift Tools/make_icon.swift Sources/Vitals/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.pngSources/Vitals/
VitalsApp.swift App entry: MenuBarExtra(.window) + Settings scene
Core/
SensorHub.swift @MainActor @Observable state machine; sampling loop and alert scheduling
Samplers.swift mach / getifaddrs / sysctl / IOKit low-level sampling
ProcessSampler.swift sysctl(KERN_PROC_ALL) + proc_pid_rusage process sampling
GPUSampler.swift IOAccelerator driver statistics and missing-value validation
BatteryKit.swift IOPowerSources battery reads
AlertEngine.swift Pure-logic alert engine (threshold + duration + cooldown)
Models.swift CPUTicks / CPUMath / DiskMath and other models and pure functions
LaunchAtLogin.swift SMAppService wrapper
Localization.swift Language selection, resource lookup and formatting
Units.swift, Ring.swift
UI/
PopoverView.swift Popover entry
PixelDashboardView.swift Pixel dashboard, overview / details navigation
PixelArt.swift Hand-drawn pixel glyphs and the isometric host illustration
PixelDetailsView.swift Full details in the same pixel style
PixelControls.swift Pixel controls shared by settings and details
LocalizedContent.swift Cross-window language and locale environment
MenuBarLabelView.swift Menu bar label (four modes)
SparklineRenderer.swift CoreGraphics mini curve and V-shaped vector mark
SettingsView.swift Pixel settings page
SettingsPresenter.swift Settings window activation, first show and bring-existing-to-front
Theme.swift
Intents/
ToggleMonitoringIntent.swift
Sampling cadence: CPU / GPU / memory / network / system load / disk I/O refresh every sampling interval; battery refreshes every 5 cycles, disk capacity every 30 cycles, and processes every 5 seconds, to keep resident cost down.
- Process names come from
kinfo_proc.kp_proc.p_command are capped at 16 characters; longer names are truncated - Process CPU usage relies on
proc_pid_rusage, which may return no data for processes owned by other users, so they are skipped - GPU data depends on the IOKit PerformanceStatistics a driver provides — not a stable cross-machine interface; unavailable fields are N/A, never 0
- GPU memory in use / allocated are driver counters that may overlap system memory categories; they cannot be summed or used to derive dedicated VRAM
- Temperature, fan speed, clock frequency and power draw have no reliable source and show N/A; thermal state is not converted to degrees Celsius
- Memory usage is an estimate from kernel page counts and is not guaranteed to match Activity Monitor exactly; memory pressure reads that fail show "unavailable"
- Disk throughput is the sum across readable devices, including disk images, and does not represent a single startup volume; a baseline is established on new attach, counter reset and resumed sampling to avoid false spikes
- The pixel host is a decorative illustration and does not depict the current Mac's enclosure; its indicator pixels track real CPU usage
- The pixel overview and details use no continuous animation and update only with real samples
Debug builds accept --debug-panel to show the popover content in an ordinary window; add --debug-dark to check the dark appearance. These entry points are not part of Release builds.
The popover is 680 × 500 and uses a fixed dark pixel dashboard. The top holds the host illustration, per-core bars and GPU history; the middle is the memory segment bar; the bottom is network, disk and system. The process ranking lives on the details page. DETAILS opens a scrollable details page and MONITOR returns to the overview; pause and settings are always reachable.
Style reference: pixel system monitor panel. All graphics are drawn with native SwiftUI / Canvas; copy comes from Resources/en.lproj and Resources/zh-Hans.lproj. Short English titles use pixel glyphs, while Chinese or missing-glyph text falls back to a crisp system font. GPU figures are real readings reported by the local driver; the power, frequency and temperature figures in the reference are neither copied nor simulated.
vm.swapusageis read through thexsw_usagebinary structure, with fields in bytes.- Process CPU time is first converted to nanoseconds via
mach_timebase_info, then converted through a time delta; one fully used logical core is 100%, and multithreaded processes can exceed 100%. See Apple XNU task.c for the conversion. - The memory pressure sysctl returns dispatch flags (1 / 2 / 4), which differ from the kernel-internal enumeration. See Apple XNU kern_memorystatus_notify.c.
- Disk throughput is based on deltas of the devices' cumulative counters, matched by stable device ID and then summed. Apple's description of the bytes-read counter.
Module toggles control both the overview and details. Turning off GPU / disk I/O also stops the corresponding sampling; CPU / memory keep supplying the menu bar and alerts. With multiple GPUs the overview shows the busiest device, and switching devices rebuilds GPU history while details list every device. Performance / efficiency core counts only show the totals the system reports and do not infer per-core numbering.
Vitals is released under the MIT License.
Run bash scripts/package-app.sh to generate the Xcode project, build the Apple
silicon app and place a locally signed copy at dist/Vitals.app. Quit Vitals
before replacing an installed copy.
The app uses an ad-hoc signature for local builds and is not notarized. GitHub Actions runs the tests and packages a local build on macOS 26; it does not publish releases.
