A Windows screen recorder with cinematic cursor-following zoom — built with Python and PySide6 (Qt 6).
Record your screen or any individual window, then export a polished MP4 video where the camera smoothly follows and zooms into your cursor movements. Perfect for tutorials, demos, and product walkthroughs.
New here? Jump to the Quickstart Guide to get recording in under 5 minutes.
github.mp4
| Document | Description |
|---|---|
| User Guide | Complete feature reference — every feature explained |
| Quickstart Guide | Install, record, edit, export — step by step |
| Architecture Guide | How the codebase works: data flow, zoom engine, capture pipeline |
| Contributing Guide | Dev setup, coding conventions, release process |
- Screen & Window Recording — Capture any monitor (hardware-accelerated via Windows Graphics Capture) or individual windows
- Smart Auto-Zoom — Automatically detects mouse settlements, typing bursts, and click clusters to generate zoom keyframes with configurable sensitivity (Low / Medium / High). Spatial-aware clustering merges nearby same-area events into sustained zooms, and consecutive clusters are chained together (up to 4 per chain) — the camera stays zoomed in and pans smoothly between them instead of zooming out and back in
- Manual Zoom Keyframes — Right-click the timeline or preview to add zoom points; drag segments to reposition them
- Zoom Depth Control — Right-click a zoom segment to set depth (Subtle 1.25×, Medium 1.5×, Close 2×, Detail 2.5×)
- Centroid Editing — Reposition the pan center of any zoom keyframe by clicking "Set centroid" on a zoom segment, then clicking the target point on the preview
- Live Zoom Shortcuts —
Ctrl+Shift+=/Ctrl+Shift+-to zoom in/out during recording (global hotkeys) - Mouse & Click Tracking — Records cursor position at 60 Hz, keyboard events, and click events with visual markers
- Click Selection & Deletion — Select individual click events on the timeline and delete unwanted ones
- Timeline Editor — Visual timeline with mouse-speed heatmap, gradient-styled zoom segments, draggable edges, and click-to-seek
- Trimming — Drag trim handles on the timeline edges to cut unwanted content from the start or end of your recording; export respects the trimmed range
- Undo & Redo — Full undo/redo for all zoom keyframe changes (Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y), up to 50 levels deep
- Cinematic Export — H.264 MP4 or GIF via ffmpeg with ease-out easing, cursor rendering, click ripple effects, and device frame overlays. MP4 supports GPU-accelerated encoding (NVENC, QuickSync, AMF) with automatic detection and fallback to software x264. GIF uses palette-based encoding (
palettegen+paletteuse) for accurate colours and bayer dithering. Status bar shows active format/encoder during export - Output Dimensions — Choose from Auto, 16:9, 3:2, 4:3, 1:1, or 9:16 aspect ratios for the exported video. Preview shows crop boundaries with a semi-transparent overlay
- Background Presets — 84 backgrounds (39 solids + 37 gradients + 8 patterns) with category picker
- Device Frames — 5 frame styles: Wide Bezel, Slim Bezel, Thin Border, Shadow Only, No Frame
- Project Files — Save/load
.fcprojbundles (ZIP with metadata + raw video) to resume editing later. Ctrl+S re-saves to the current file. Title bar shows project name and unsaved-changes indicator (●) - Close Confirmation — Prompts to save unsaved changes before closing the app
- Open in Clipchamp — One-click handoff to Clipchamp for further editing
- Debug Overlay — Per-time zoom marker overlay on the preview for fine-tuning keyframes (toggle via ⚙ settings menu)
- 3-Second Countdown — Visual countdown before recording starts
- Frameless Dark UI — Custom title bar, dark theme with purple accents
| Layer | Technology | Purpose |
|---|---|---|
| UI Framework | PySide6 (Qt 6) | Widgets, layout, painting, signals/slots |
| Screen Capture | Windows Graphics Capture (WGC) | Hardware-accelerated monitor/window capture |
| Window Capture | Win32 PrintWindow (ctypes) | Per-window capture without bleed-through |
| Recording Pipe | ffmpeg via imageio-ffmpeg | Lossless intermediate AVI (huffyuv) piped via stdin |
| Video Export | ffmpeg (libx264 / HW accel / GIF) | H.264 MP4 or GIF export with zoom/cursor baked in; MP4 auto-detects NVENC, QuickSync, AMF; GIF uses palettegen + paletteuse |
| Image Processing | OpenCV + NumPy | Frame manipulation, thumbnails, cursor rendering |
| Input Tracking | Win32 Hooks (ctypes) | Low-level mouse, keyboard, and click tracking |
| Zoom Engine | Pure Python | Ease-out-eased keyframe interpolation |
- Windows 10/11
- Python 3.10+ — Download (check "Add to PATH" during install)
dev.batCreates a virtual environment, installs dependencies, and launches the app.
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
python main.pyBuild a standalone .exe with PyInstaller:
build.batOutput: dist\FollowCursor\FollowCursor.exe
The build script automatically creates a virtual environment and installs all dependencies if needed.
A GitHub Actions workflow (.github/workflows/build.yml) runs on every push/PR to main:
- Sets up Python 3.13 on a Windows runner
- Extracts the app version from
app/version.py - Installs dependencies
- Builds with PyInstaller
- Uploads the versioned build artifact (retained 30 days)
- Creates a GitHub Release when a version tag (
v*) is pushed
You can also trigger a build manually from the Actions tab.
- Update
__version__infollowcursor/app/version.py - Commit and push to
main - Tag the commit:
git tag v0.2.0 && git push --tags - The CI workflow automatically creates a GitHub Release
| Shortcut | Action |
|---|---|
Space |
Play / pause playback (edit view) |
Z |
Insert zoom keyframe at playhead (edit view) |
Ctrl+Shift+= |
Zoom in at cursor position |
Ctrl+Shift+- |
Zoom out to 1.0× |
Ctrl+S |
Save project (re-saves to current file) |
Ctrl+Z |
Undo last zoom keyframe change |
Ctrl+Shift+Z / Ctrl+Y |
Redo last undone change |
| Right-click zoom segment | Edit depth / centroid / delete |
| Right-click preview | Add zoom at click position |
| Drag zoom segment edge | Reposition zoom in timeline |
| Left-click event dot | Select click event |
Delete |
Remove selected click event |
| Drag timeline edge handles | Set trim start/end point |
followcursor/
├── main.py # Entry point, QApplication setup
├── requirements.txt # Python dependencies
├── build.bat # PyInstaller build script
├── dev.bat # Dev setup & launch script
├── followcursor.ico # App icon
├── app/
│ ├── version.py # Semantic version (__version__)
│ ├── models.py # Data classes (MousePosition, ZoomKeyframe, ClickEvent, …)
│ ├── utils.py # Shared helpers (ffmpeg path, encoder detection, subprocess, time formatting)
│ ├── zoom_engine.py # Ease-out keyframe interpolation
│ ├── mouse_tracker.py # QTimer-based cursor polling (60 Hz)
│ ├── keyboard_tracker.py # Win32 keyboard hook
│ ├── click_tracker.py # Win32 mouse click hook
│ ├── activity_analyzer.py # Auto-zoom from mouse/keyboard/click activity
│ ├── screen_recorder.py # WGC + ffmpeg pipe in background thread
│ ├── window_utils.py # Win32 window enumeration & PrintWindow
│ ├── video_exporter.py # H.264 MP4 export with zoom & cursor
│ ├── compositor.py # QPainter compositing (frame + background)
│ ├── cursor_renderer.py # Arrow cursor + click ripple rendering
│ ├── global_hotkeys.py # Win32 RegisterHotKey via QThread
│ ├── backgrounds.py # 84 background presets (solids, gradients, patterns)
│ ├── frames.py # 5 device frame presets
│ ├── project_file.py # .fcproj save/load (ZIP bundle)
│ ├── icon.py # QPainter-generated app icon
│ ├── theme.py # QSS dark theme stylesheet
│ ├── main_window.py # Main window — assembles all widgets
│ └── widgets/
│ ├── title_bar.py # Custom frameless title bar
│ ├── source_picker.py # Screen/Window selection dialog (tabbed)
│ ├── preview_widget.py # Live / playback preview with zoom/pan
│ ├── timeline_widget.py # QPainter timeline with heatmap & keyframes
│ ├── editor_panel.py # Zoom settings, background, frame, output pickers
│ ├── countdown_overlay.py # 3-2-1 countdown animation
│ ├── processing_overlay.py # Pulsing banner while finishing recording
│ └── recording_border.py # Red border during recording
