CLI · Rust crate · v0.7.0
aether-renderer-core
PNG frames in, transparent video out. A folder, a ZIP or a stream on stdin becomes WebM, MOV, GIF or MP4, with the alpha intact, fades that go to transparent, and real progress.
Install
You need FFmpeg on your PATH:
brew install ffmpeg # macOS
sudo apt install ffmpeg # Ubuntu / Debian
winget install Gyan.FFmpeg # WindowsThen install with Cargo, or download a binary:
cargo install aether-renderer-coreUsage
# folder of PNGs -> transparent WebM at 30 fps
aether-renderer-core -i frames/ -o out.webm
# from a zip, as a GIF at 24 fps
aether-renderer-core -i frames.zip -o out.gif --fps 24
# for a video editor, with a soft 1 second fade in and out
aether-renderer-core -i frames/ -o out.mov --fade-in 1 --fade-out 1
# MP4 on the colour of the page it will sit on
aether-renderer-core -i frames/ -o out.mp4 --background '#1e1e1e'
# check a frame first: saves the middle frame as out.png
aether-renderer-core -i frames/ -o out.webm --preview
# PNG files back to back on stdin, straight from capture
aether-renderer-capture -i anim.html --stdout -d 4 | aether-renderer-core -i - -o out.webm --frames 120Which format?
| Format | Transparency | Best for |
|---|---|---|
webm | ✅ | Websites (Chrome, Firefox, Edge), small files. The default. |
mov | ✅ full quality | Video editors: Premiere, DaVinci Resolve, Final Cut, After Effects |
gif | ✅ on/off per pixel | Chat, email, README files, anywhere video isn’t allowed |
mp4 | ❌ | Social media and maximum compatibility |
The format follows the output file name, or set it with --format. Safari doesn't play transparent WebM; use GIF there, or MP4 on a matching --background.
All options
| Option | Default | What it does |
|---|---|---|
-i, --input | required | Folder or .zip with the frames, or - for a PNG stream on stdin |
--frames | With -i -: how many frames the stream has (real progress; needed for --fade-out) | |
-o, --output | output.<format> | Output file |
--format | from the extension, else webm | webm, mov, gif or mp4 |
--fps | 30 | Frames per second |
--file-pattern | *.png | Glob (scene_*.png) or numbered sequence (frame_%04d.png) |
--background | transparent (black for mp4) | Solid colour behind the frames: '#1e1e1e', white, transparent |
--fade-in / --fade-out | 0 | Seconds to fade from / to transparent (or the background colour) |
--crf | webm 30, mp4 23 | Constant quality, lower is better |
--bitrate | Target bitrate instead, e.g. 2M (--crf wins if both are set) | |
--preview [N] | Save frame N (default: the middle one) as a PNG instead of rendering | |
-c, --config | JSON or TOML config file (the command line wins) | |
--progress json | One JSON object per line on stdout, for GUIs and scripts | |
-v, --verbose | Details and a progress bar |
Progress for other programs
With --progress json, every line on stdout is a JSON object, so a GUI or script can draw its own bar:
{"event":"progress","frame":0,"total_frames":360,"percent":0.0}
{"event":"progress","frame":96,"total_frames":360,"percent":26.67}
{"event":"progress","frame":360,"total_frames":360,"percent":100.0}As a library
[dependencies]
aether-renderer-core = "0.7"use aether_renderer_core::config::PartialConfig;
let cfg = PartialConfig {
input: Some("frames".into()),
output: Some("out.webm".into()),
fps: Some(24),
..Default::default()
}
.into_config()?;
let report = aether_renderer_core::render_with_progress(cfg, |p| {
println!("{:.0}% ({}/{} frames)", p.percent, p.frame, p.total_frames);
})?;
println!("{}", report.summary());render_stream renders PNG files from any reader (no temp files), which is how the desktop app encodes a capture. Full API on docs.rs.