v1.2.0 Now a desktop app: Flatpak bundle and app menu entry

The camera follows your cursor. On Wayland, in 56 MB.

HyperGSR is a screen recorder for Linux, written in Rust. It reads the cursor straight from the GPU’s hardware cursor plane without root, then moves a critically damped spring camera to where you work. Encoding runs on NVENC or VAAPI.

Ubuntu · Fedora · Arch

              
            
No root, no XWayland pointer MIT license Wayland and X11
Workbench

Move your cursor. Stop on something. Watch the camera settle.

This is the same spring model HyperGSR uses, running in your browser at fixed 240 Hz substeps. Pause on a region for a quarter second and the camera zooms in. Move fast and it pulls back.

240 Hz solver
Input · full framesrc/cursor.rs
Export MP4
Move the pointer here · click to pulse
Output · camerasrc/zoom_engine.rs
Cursor0.50, 0.50
Camera0.50, 0.50
Zoom1.00×
|v|0.000
PhaseIdle
Steps/s240
Defaults match ZoomConfig::default(): k = 60, zoom 2.00×, hold 1650 ms, deadzone 0.065. Read the zoom engine
Measured

Small enough to leave running.

Numbers come from a 1080p60 recording on a hybrid AMD Radeon and NVIDIA RTX 3050 laptop under COSMIC, with NVENC preset P4.

Resident memory
56MB

Native Rust and GTK4. No browser engine, no plugin host.

CPU at 1080p60
<1.5%

Encoding runs on NVENC. The CPU copies each frame once and steps the camera.

Cursor source
#92

DRM hardware cursor plane, read at 60 Hz through libdrm.

Camera solver
240Hz

Critically damped, so it settles without overshoot or wobble.

Your numbers will depend on GPU, compositor, and resolution. Run gpu-recorder-rs --report to see the encoders and cursor planes it finds.

Architecture

Three parts. Each one is a file you can read.

HyperGSR started as a reverse-engineering pass over gpu-screen-recorder 6.1.4. What it kept, what it replaced, and why is written up in RE_ANALYSIS.md. The longer story is in reading the cursor from the GPU on DEV.

01 / Cursor

Read the cursor from the GPU, not the compositor.

Wayland hides the global pointer from clients, and XWayland only sees its own windows. HyperGSR asks the kernel instead: the hardware cursor plane already holds the exact position the display controller is drawing.

libdrm.so.2UNIVERSAL_PLANESATOMIC
Inspect src/cursor.rs
src/cursor.rsquery_cursor()
for j in 0..pcount {
    let name = prop_name(j);
    if name.starts_with(b"FB_ID\0") {
        fb_id = pval;
    } else if name.starts_with(b"CRTC_X\0") {
        crtc_x = Some((pval as u32) as i32);
    } else if name.starts_with(b"CRTC_Y\0") {
        crtc_y = Some((pval as u32) as i32);
    }
}
// fb_id changes when the cursor shape changes
let nx = (cx as f32 / self.screen_w).clamp(0.0, 1.0);
02 / Camera

A spring with exactly enough damping.

Pan and zoom are three springs with damping set to 2√k, the critical point. The camera reaches its target as fast as it can without overshooting. Pan runs at 0.78 of zoom stiffness so it glides a little behind.

ζ = 1.0k_pan = 0.78 · kUV edge clamp
Inspect src/zoom_engine.rs
src/zoom_engine.rsVirtualCamera::step()
let k_zoom = cfg.spring_stiffness.clamp(20.0, 260.0);
let c_zoom = 2.0 * k_zoom.sqrt();
let k_pan  = (k_zoom * 0.78).clamp(18.0, 220.0);
let c_pan  = 2.0 * k_pan.sqrt();

let ax = -k_pan * (self.cam_x - self.target_x) - c_pan * self.vel_x;
let az = -k_zoom * (self.cam_zoom - self.target_zoom)
         - c_zoom * self.vel_zoom;

self.vel_x += ax * dt;
self.cam_x += self.vel_x * dt;
// clamp the centre so the crop never samples outside [0, 1]
03 / Capture

A small C helper for the portal, then the GPU encoder.

The ScreenCast portal hands over a PipeWire stream. A standalone helper negotiates DMA-BUF or MemFd buffers and feeds them to FFmpeg with NVENC or VAAPI. The portal restore token is reused, so you pick a screen once.

xdg-desktop-portalPipeWireh264_nvenc · p4
Inspect src/pw_portal_capture.c
src/pw_portal_capture.con_param_changed()
SPA_PARAM_BUFFERS_dataType,
  SPA_POD_CHOICE_FLAGS_Int(
    (1 << SPA_DATA_MemFd) |
    (1 << SPA_DATA_MemPtr) |
    (1 << SPA_DATA_DmaBuf))

/* encoder pipe */
"-c:v h264_nvenc -preset p4 -tune hq"
" -rc vbr -cq 19 -b:v 0 -pix_fmt yuv420p"
" -movflags +faststart"
Compare

Where it fits next to the tools you already use.

OBS and gpu-screen-recorder are excellent general recorders. HyperGSR focuses on one thing they don’t do out of the box: a camera that follows the cursor on Wayland.

Capability HyperGSR gpu-screen-recorder OBS Studio Electron studio recorders
Cursor-following auto-zoomRecorded into the MP4 ✓ Spring camera — Plugin ✓
Global cursor on native WaylandWithout root ✓ DRM cursor plane — — Varies
Custom zoom combosMouse, modifier+click, shake, key ✓ — Hotkeys Varies
Hardware encodingNVENC, VAAPI ✓ ✓ ✓ Varies
Resident memory at 1080p60 56 MB Not measured 340 MB 650+ MB
License MIT GPL-3.0 GPL-2.0+ Varies

Memory figures are resident set size measured on the same machine during a 1080p60 recording. “Plugin” means a third-party plugin exists but is not part of the default install.

Install

From clone to first recording in three commands.

The installer detects apt, dnf, or pacman, installs GTK4, gtk4-layer-shell, libdrm, PipeWire, and FFmpeg, then builds a release binary.

Step 01

Clone and install

Asks for sudo once, for system packages only.

shell
git clone \
  https://github.com/KunalSiyag/gpu-recorder-rs
cd gpu-recorder-rs
./install.sh
Step 02

Run the island

Pick a screen once. The portal remembers it.

shell
cargo build --release
./target/release/gpu-recorder-rs

# light theme, studio open
HYPERGSR_THEME=light HYPERGSR_EXPANDED=1 \
  ./target/release/gpu-recorder-rs
Step 03

Script it

Every UI action is also a flag.

shell
# 5-second recording with auto-zoom
gpu-recorder-rs --record-seconds 5

# encoders and cursor planes, as JSON
gpu-recorder-rs --report
FAQ

Questions people ask first.

Does it need root?
No. It only reads cursor plane properties from /dev/dri/card*, which the logged-in desktop user can already open. HyperGSR never becomes DRM master and never changes display modes.
Which compositors work?
It is developed and tested on COSMIC. It should work on any compositor with the ScreenCast portal and a hardware cursor plane, such as GNOME, KDE Plasma, Hyprland, or Sway. The island uses zwlr_layer_shell_v1 when the compositor supports it and opens as a regular window otherwise. Reports from other compositors are welcome as issues.
What if my compositor draws the cursor in software?
Then there is no cursor plane to read. HyperGSR falls back to the X11 pointer through XQueryPointer. That is exact on Xorg, but under XWayland it only follows the cursor over XWayland windows. --report shows which source it found.
Which GPUs are supported?
NVIDIA through NVENC (H.264, HEVC, AV1) and AMD or Intel through VAAPI (H.264, HEVC). If neither is available it falls back to x264 on the CPU. It is developed on a hybrid AMD Radeon and NVIDIA RTX 3050 laptop.
Can I zoom by hand instead of automatically?
Yes. Open the studio, choose Record Combo…, and press the mouse button, modifier-click, or key you want. Pick toggle lock, hold to zoom, or step through 1.8× and 2.5×. The scroll wheel adjusts zoom while locked.
How is this related to gpu-screen-recorder?
It began by reverse-engineering gpu-screen-recorder 6.1.4 to understand its capture path, including a hardcoded /var/lib/flatpak path that breaks user installs. HyperGSR is a separate Rust codebase and does not ship gpu-screen-recorder code.