In this section you will learn how to get information about installed graphics controllers and connected displays.
In version 6 the former graphics() function was split into two separate functions: gpu() (graphics controllers) and displays() (monitors / displays). Both return plain arrays.
On Windows, displays() reports physical monitors: in duplicate/mirror mode each mirrored monitor gets its own entry (sharing the resolution and position of the mirrored screen). All displays of such a mirrored set are flagged with mirror: true (Linux, macOS and Windows).
For function reference and examples we assume, that we imported systeminformation as follows:
const si = require('systeminformation');
Graphics Controllers
All functions in this section return a promise.
| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments |
|---|---|---|---|---|---|---|---|
| si.gpu() | [ { ...}] | X | X | X | array of graphics controllers | ||
| [0].vendor | X | X | X | e.g. ATI | |||
| [0].model | X | X | X | graphics controller model | |||
| [0].bus | X | X | X | on which bus (e.g. PCIe) | |||
| [0].busAddress | X | X | PCI bus address, e.g. '01:00.0' join key for displays()[].gpuBusAddress |
||||
| [0].vram | X | X | X | VRAM size (in MB), null when the GPU shares system memory | |||
| [0].vramDynamic | X | X | X | true if dynamically allocated ram (integrated GPUs, see notes) | |||
| [0].deviceId | X | (macOS only) - device ID | |||||
| [0].vendorId | X | (macOS only) - vendor ID | |||||
| [0].external | X | (macOS only) - is external GPU | |||||
| [0].cores | X | (Apple silicon only) - GPU cores | |||||
| [0].metalVersion | X | (macOS only) - Metal Version | |||||
| [0].subDeviceId | X | X | (optional nvidia-smi) - sub device ID | ||||
| [0].driverVersion | X | X | (optional nvidia-smi) - driver version | ||||
| [0].name | X | X | (optional nvidia-smi) - name | ||||
| [0].pciBus | X | X | (optional nvidia-smi) - PCI bus ID | ||||
| [0].fanSpeed | X | X | (optional nvidia-smi) - fan speed | ||||
| [0].memoryTotal | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - memory total | ||||
| [0].memoryUsed | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - memory used | ||||
| [0].memoryFree | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - memory free | ||||
| [0].utilizationGpu | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - utilization GPU | ||||
| [0].utilizationMemory | X | X | (optional nvidia-smi) - utilization memory | ||||
| [0].temperatureGpu | X | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes; macOS: optional macos-temperature-sensor) - temperature GPU | |||
| [0].temperatureMemory | X | X | (optional nvidia-smi) - temperature memory | ||||
| [0].powerDraw | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - power draw | ||||
| [0].powerLimit | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - power limit | ||||
| [0].clockCore | X | X | (optional nvidia-smi; Linux: DRM sysfs - see notes) - clock core | ||||
| [0].clockMemory | X | X | (optional nvidia-smi) - clock memory | ||||
Example
[
{
vendor: 'Intel',
model: 'Intel Iris Plus Graphics 655',
bus: 'Built-In',
vram: 1536,
vramDynamic: true
}
]
|
|||||||
GPU metrics on Linux (Intel, AMD, NVIDIA)
Beside the optional nvidia-smi, gpu() reads the runtime values the kernel exposes per DRM card below /sys/class/drm/card*. This needs no additional tool and no root privileges, and it is matched to the controller by its PCI bus address. Values already delivered by nvidia-smi are never overwritten.
| Attribute | Source | Intel (i915 / xe) | AMD (amdgpu) |
|---|---|---|---|
| clockCore | gt_act_freq_mhz, gt_cur_freq_mhz, tile0/gt0/freq0/act_freq, pp_dpm_sclk | X | X |
| temperatureGpu | device/hwmon/hwmon*/temp1_input | X (discrete cards; most iGPUs have no own sensor) | X |
| powerDraw / powerLimit | device/hwmon/hwmon*/power1_input, power1_max | X (platform dependent) | X |
| utilizationGpu | device/gpu_busy_percent | - | X |
| memoryTotal / memoryUsed / memoryFree | device/mem_info_vram_total, mem_info_vram_used, lmem_total_bytes, tile0/physical_vram_size_bytes | X (discrete Arc only - an iGPU has no VRAM) | X |
Intel GPU utilization is the one value that cannot be read this way: the i915 / xe drivers publish their engine busy counters through the perf / PMU interface instead of sysfs (#890). Every tool reading it - intel_gpu_top -J from intel-gpu-tools, nvtop - therefore needs root or the CAP_PERFMON capability, which a library like this cannot require. If you need the busy percentage of an Intel GPU, run intel_gpu_top yourself with the appropriate privileges; frequency, power and temperature are available through gpu().
Where such a node exists it also sets vram, in preference to the memory region size lspci reports. That region is only an approximation and overshoots on cards with resizable BAR - a Radeon 7900 XTX exposes a 32 GB window for 24 GB of memory (#1035). Values already delivered by nvidia-smi stay untouched.
Integrated GPUs and vram
An integrated GPU has no dedicated video memory - it takes what it needs from system RAM. lspci still reports memory regions for it, but the largest one is the graphics aperture (the CPU visible window into the GTT, typically 256 MB on an Intel iGPU and configurable in the firmware), not video memory. Reporting it as vram made an Intel UHD Graphics 630 look like a 256 MB card.
A card is treated as sharing system memory when its DRM driver is i915 or xe and sysfs reports no VRAM size for it - either because no such node exists, or because it reads 0. It then returns vram: null with vramDynamic: true. The integrated GPUs of ARM SoCs are not covered here: they are platform devices rather than PCI ones and come from their own code paths (vcgencmd on a Raspberry Pi, the Tegra sysfs nodes on a Jetson).
The driver name alone does not decide it: i915 and xe also drive the discrete Arc cards, which do expose their memory (lmem_total_bytes, tile0/physical_vram_size_bytes). Discrete cards are therefore unaffected - AMD through mem_info_vram_total, NVIDIA through nvidia-smi.
NVIDIA Tegra / Jetson
On a Jetson the GPU is integrated into the SoC and attached through the device tree, not through PCI. lspci therefore does not list it, and the DRM sysfs path above skips it because it has no PCI bus address - which used to leave gpu() empty on every Tegra board. The values now come from the Tegra sysfs nodes directly:
| Attribute | Source |
|---|---|
| model | /proc/device-tree/model, e.g. "NVIDIA Jetson AGX Orin Developer Kit" - falls back to the SoC name from compatible (e.g. Tegra234) |
| utilizationGpu | /sys/devices/platform/gpu.0/load or /sys/devices/gpu.0/load (per mille, the same value tegrastats shows as GR3D_FREQ) |
| temperatureGpu | the thermal zone whose type starts with gpu - GPU-therm on TX2 / Xavier, gpu-thermal on Orin |
| clockCore | /sys/class/devfreq/*.gpu|*.gv11b|*.ga10b/cur_freq |
This needs no root privileges and neither tegrastats nor jtop. tegrastats is deliberately not used: it is a streaming process that prints one line per interval until it is killed, its output format differs between Tegra generations and L4T releases, and on several releases it requires root - while it only reads the same sysfs nodes itself.
The GPU has no dedicated memory, it shares system RAM. vram is therefore null with vramDynamic: true; use mem() for the actual memory situation. powerDraw is not reported: the INA3221 power rails of a Jetson depend on the carrier board, not only on the SoC generation. Any node a generation does not provide is simply left out rather than guessed.
Displays
| Function | Result object | Linux | BSD | Mac | Win | Sun | Comments |
|---|---|---|---|---|---|---|---|
| si.displays() | [ { ...}] | X | X | X | array of monitors / displays | ||
| [0].vendor | X | monitor/display vendor | |||||
| [0].vendorId | X | (macOS only) - monitor/display vendor ID | |||||
| [0].deviceName | X | e.g. \\.\DISPLAY1 | |||||
| [0].model | X | X | X | monitor/display model | |||
| [0].productionYear | X | X | production year | ||||
| [0].serial | X | X | serial number | ||||
| [0].displayId | X | X | display ID | ||||
| [0].main | X | X | X | true if main monitor | |||
| [0].mirror | X | X | X | true if mirrored / duplicated display | |||
| [0].builtin | X | X | true if built-in monitor | ||||
| [0].connection | X | X | X | e.g. DisplayPort, HDMI | |||
| [0].sizeX | X | X | size in mm horizontal | ||||
| [0].sizeY | X | X | size in mm vertical | ||||
| [0].pixelDepth | X | X | X | color depth in bits | |||
| [0].resolutionX | X | X | X | pixel horizontal | |||
| [0].resolutionY | X | X | X | pixel vertical | |||
| [0].currentResX | X | X | X | current pixel horizontal | |||
| [0].currentResY | X | X | X | current pixel vertical | |||
| [0].positionX | X | X | X | display position X | |||
| [0].positionY | X | X | X | display position Y | |||
| [0].workAreaResolutionX | X | X | X | working area (screen minus task bar / panels / menu bar and Dock) resolution X | |||
| [0].workAreaResolutionY | X | X | X | working area resolution Y | |||
| [0].workAreaPositionX | X | X | X | working area position X | |||
| [0].workAreaPositionY | X | X | X | working area position Y | |||
| [0].currentRefreshRate | X | X | X | current screen refresh rate | |||
| [0].scale | X | DPI scaling factor, e.g. 1.5 for 150% | |||||
| [0].powerState | X | X | 'on', 'standby', 'suspend', 'off' or '' when unknown Linux per connector (DPMS), Windows system wide macOS: Intel only, Apple Silicon reports '' |
||||
| [0].gpu | X | X | model of the GPU driving this display macOS and Windows only |
||||
| [0].gpuBusAddress | X | X | PCI address of the GPU driving this display matches gpu()[].busAddress Windows resolves it from the pnp location info |
||||
Displays on LinuxDisplay detection uses xrandr, which needs a running X session. Without a display server - a headless server, or Wayland without XWayland - the DRM connectors in /sys/class/drm are read instead. That fallback reports every connected connector with its name, resolution and, where the monitor provides an EDID, model and physical size. Values that only a window manager knows - position, work area, refresh rate and pixel depth - stay null there, and since there is no "primary" display either, main goes to the built-in panel, or to the first connector when there is none. Example
[
{
vendor: '',
model: 'Color LCD',
main: true,
mirror: false,
builtin: false,
connection: 'Internal',
sizeX: null,
sizeY: null,
pixelDepth: 24,
resolutionX: 2560,
resolutionY: 1600,
currentResX: 2560,
currentResY: 1600,
positionX: 0,
positionY: 0,
workAreaResolutionX: 2560,
workAreaResolutionY: 1561,
workAreaPositionX: 0,
workAreaPositionY: 39,
currentRefreshRate: null,
scale: null
}
]
|
|||||||