framewatch
A screen recorder that only writes a frame when something worth keeping happens.
cargo install framewatch --features wgc
cargo install framewatch --features macos
cargo install framewatch --features linux-x11
cargo add framewatch --no-default-features
Add gui for the window picker, or record for narrated capture. On macOS you’ll be asked for Screen Recording permission the first time it runs. Wayland isn’t supported yet.
One session
Five seconds of tests, three frames worth keeping.
If you record a screen continuously, a coding agent drowns in near-identical frames. If you take a screenshot on demand, you miss everything that happened in between. framewatch watches one window and saves a frame only when something meaningful happens.
$ cargo test Compiling framewatch v0.8.5 running 10 tests test engine_static::static_window_emits_only_initial ... ok test engine_static::single_change_then_static_settles_once ... ok test engine_spinner::spinner_collapses_to_busy_then_settled ... ok test engine_value::volatile_region_is_throttled_not_per_frame ... ok test engine_auto_spinner::auto_spinner_collapses_to_busy_then_settled ... ok test engine_auto_spinner::scattered_churn_is_not_an_auto_spinner ... ok test engine_sustained_motion::sustained_change_produces_periodic_keyframes ... ok test dedup::identical_busy_end_frame_is_deduped ... ok test crop::crop_clamps_and_handles_out_of_bounds ... ok test crop::watch_with_crop_produces_cropped_images ... ok test result: ok. 10 passed; 0 failed
.framewatch/2026-06-14T06-22-17_Terminal/timeline.jsonl
- {"seq":0,"kind":"initial","elapsed_ms":0,"image":"frames/000000_initial.png","note":"Session start."}
- {"seq":1,"kind":"busy_start","elapsed_ms":1200,"image":null,"note":"Test runner started (spinner active)."}
- {"seq":2,"kind":"busy_end","elapsed_ms":4830,"image":"frames/000002_busy_end.png","coalesced_frames":71,"note":"Spinner stopped after 3.63s; 71 animation frames collapsed."}
- {"seq":3,"kind":"settled","elapsed_ms":5180,"image":"frames/000003_settled.png","note":"Settled: test results rendered."}
-
initial 0.00s
$ cargo test -
busy_end 4.83s
$ cargo test Compiling framewatch v0.8.5 running 10 tests test engine_static::static_window_emits_only_initial ... ok test engine_static::single_change_then_static_settles_once ... ok test engine_spinner::spinner_collapses_to_busy_then_settled ... ok test engine_value::volatile_region_is_throttled_not_per_frame ... ok test engine_auto_spinner::auto_spinner_collapses_to_busy_then_settled ... ok test engine_auto_spinner::scattered_churn_is_not_an_auto_spinner ... ok test engine_sustained_motion::sustained_change_produces_periodic_keyframes ... ok test dedup::identical_busy_end_frame_is_deduped ... ok test crop::crop_clamps_and_handles_out_of_bounds ... ok test crop::watch_with_crop_produces_cropped_images ... ok
-
settled 5.18s
$ cargo test Compiling framewatch v0.8.5 running 10 tests test engine_static::static_window_emits_only_initial ... ok test engine_static::single_change_then_static_settles_once ... ok test engine_spinner::spinner_collapses_to_busy_then_settled ... ok test engine_value::volatile_region_is_throttled_not_per_frame ... ok test engine_auto_spinner::auto_spinner_collapses_to_busy_then_settled ... ok test engine_auto_spinner::scattered_churn_is_not_an_auto_spinner ... ok test engine_sustained_motion::sustained_change_produces_periodic_keyframes ... ok test dedup::identical_busy_end_frame_is_deduped ... ok test crop::crop_clamps_and_handles_out_of_bounds ... ok test crop::watch_with_crop_produces_cropped_images ... ok test result: ok. 10 passed; 0 failed
- A recorder at 14 fps would keep
- 73 frames
- framewatch keeps
- 3 frames, 4 events
When it saves a frame
Everything else gets collapsed.
Detection is heuristic and runs on every frame with no model call. You can mark where the spinners and counters live, or let it find small loading animations on its own.
initialThe first frame
So the story has a starting point.
settledWhen the window goes quiet
After a burst of change ends. This is usually the frame that matters.
busy_start · busy_endWhen a spinner starts and stops
The animation in between collapses into one count, not seventy images.
volatileWhen a counter or log tail moves
Sampled on a throttle, never once per frame.
manual · markWhen you ask
A manual trigger, or a label your app writes into the timeline.
What an agent gets
A folder it can read in order.
Read session.json, stream timeline.jsonl, and open only the images the settled and busy_end events point to. coalesced_frames says how much activity each saved image stands for.
.framewatch/<session_id>/ ├── frames/ │ ├── 000000_initial.png │ ├── 000002_busy_end.png │ └── 000003_settled.png ├── timeline.jsonl # one event per line ├── session.json # target, config, counts └── README_FOR_AGENT.md # how to read it
Quickstart
Pick a window and watch it.
The window picker needs the gui feature. Without it, point framewatch at a window by title or at a saved config.
# see what can be captured framewatch windows # pick a window and mark spinner regions framewatch gui # or run headless by title framewatch watch --title "Visual Studio Code" --out ./.framewatch
Capabilities
What it does.
Anyone whose screen recordings are 99% nothing and 1% the thing they needed.
- 01
Captures when the window settles, when a loading indicator starts or stops, and when busy regions change
- 02
Timestamped metadata with every frame explaining why it was taken
- 03
Published on crates.io, with docs on docs.rs and CI
- 04
A single Rust binary with no runtime dependencies
Specification
What it’s built with, and when.
Pulled from the repository each time this site is built.
- State
- Shipping
- Discipline
- Developer tooling
- Shape
- Installed software
- Started
- Jun 2026
- Last change
- Sep 2026
- Commits
- 42
- Rust
- Windows API
- ScreenCaptureKit
- X11
- Rust 100%
Next
Install framewatch
It’s on crates.io. Pick the capture backend for your platform. The detection engine is the same on all three.
cargo install framewatch --features wgc
cargo install framewatch --features macos
cargo install framewatch --features linux-x11
cargo add framewatch --no-default-features
Add gui for the window picker, or record for narrated capture. On macOS you’ll be asked for Screen Recording permission the first time it runs. Wayland isn’t supported yet.
Questions, or something it doesn’t do yet? Email dmoore@