Bevy 0.18.1 project that loads a vintage_terminal 3D model and drives its screen with a ratatui UI rendered through soft_ratatui.
The vintage_terminal asset is from Sketchfab: Vintage Terminal.
Current project surface:
- orbiting hero shot
- stationary zoom-in intro
- intermission/standby loop
- shutdown/outro loop
- PNG exporters for every scene
- render scripts that generate both H.264 MP4 loops and animated WebP previews
- Rust and Cargo
ffmpegandimg2webpfor the render scripts
cargo check --binsOrbiting hero shot around the terminal.
cargo run --bin rotating_terminalStationary intro shot with a one-way zoom in that settles on the final framing.
cargo run --bin stationary_terminalStandby/intermission scene with the same low camera height and radius envelope as the orbit scene, plus a faster waiting-pattern spinner on the terminal screen.
cargo run --bin intermissionShutdown/outro scene with friendly flashing farewell copy on the terminal screen.
cargo run --bin shutdown_outroAll exporters render numbered PNGs with bevy_image_export.
Shared export settings:
- internal Bevy render resolution:
3840x2160 - final encoded MP4 resolution:
1920x1080by default - output cadence:
60 fps - warmup before capture:
60frames
cargo run --release --bin export_rotationCustom output directory:
cargo run --release --bin export_rotation -- /path/to/output_dirSettings:
- frame count:
420 - duration:
7.0s - motion: one seamless full orbit loop
cargo run --release --bin export_zoomCustom output directory:
cargo run --release --bin export_zoom -- /path/to/output_dirSettings:
- frame count:
720 - duration:
12.0s - motion: slow eased zoom in, then hold on the zoomed-in framing
cargo run --release --bin export_intermissionCustom output directory:
cargo run --release --bin export_intermission -- /path/to/output_dirSettings:
- frame count:
384 - duration:
6.4s - motion: seamless intermission loop
cargo run --release --bin export_shutdownCustom output directory:
cargo run --release --bin export_shutdown -- /path/to/output_dirSettings:
- frame count:
960 - duration:
16.0s - motion: seamless shutdown ping-pong loop based on the shutdown camera and screen text cycle
The scripts below are executable and can be run directly with ./scripts/....
Default outputs:
- frames:
renders/rotation_frames - video:
renders/rotating_terminal_loop.mp4 - preview:
rotating_terminal_loop.webp
Usage:
./scripts/render_rotation_loop.sh
./scripts/render_rotation_loop.sh /path/to/frames /path/to/output.mp4
./scripts/render_rotation_loop.sh /path/to/frames /path/to/output.mp4 /path/to/output.webpDefault outputs:
- frames:
renders/zoom_frames - video:
renders/terminal_zoom_loop.mp4 - preview:
terminal_zoom_loop.webp
Usage:
./scripts/render_zoom_in.sh
./scripts/render_zoom_in.sh /path/to/frames /path/to/output.mp4
./scripts/render_zoom_in.sh /path/to/frames /path/to/output.mp4 /path/to/output.webpDefault outputs:
- frames:
renders/intermission_frames - video:
renders/intermission_loop.mp4 - preview:
intermission_loop.webp
Usage:
./scripts/render_intermission_loop.sh
./scripts/render_intermission_loop.sh /path/to/frames /path/to/output.mp4
./scripts/render_intermission_loop.sh /path/to/frames /path/to/output.mp4 /path/to/output.webpDefault outputs:
- frames:
renders/shutdown_frames - video:
renders/shutdown_loop.mp4 - preview:
shutdown_loop.webp
Usage:
./scripts/render_outro_loop.sh
./scripts/render_outro_loop.sh /path/to/frames /path/to/output.mp4
./scripts/render_outro_loop.sh /path/to/frames /path/to/output.mp4 /path/to/output.webpRuns the four scene-specific render scripts.
Default outputs:
- videos:
renders/*.mp4 - WebPs:
rotating_terminal_loop.webpterminal_zoom_loop.webpintermission_loop.webpshutdown_loop.webp
Usage:
./scripts/render_all_media.sh
./scripts/render_all_media.sh /path/to/renders_dirOverride the encoded playback framerate:
FPS=30 ./scripts/render_rotation_loop.shOverride the final encoded MP4 size:
FINAL_WIDTH=1920 FINAL_HEIGHT=1080 ./scripts/render_rotation_loop.shWebP conversion options:
WEBP_FPS=30 WEBP_SCALE_WIDTH=320 WEBP_DELAY_MS=33 WEBP_QUALITY=90 ./scripts/render_rotation_loop.shThe render scripts keep Bevy's internal frame export at 3840x2160 and downscale to the final MP4 size in ffmpeg using Lanczos filtering.
Final MP4 output size:
FINAL_WIDTH=1920 FINAL_HEIGHT=1080 ./scripts/render_rotation_loop.shEach script:
- deletes the existing frame directory
- runs the matching
export_*binary - encodes the PNG sequence to an H.264 MP4 with
ffmpeg - converts the MP4 to an animated WebP with
img2webp
Encoding settings:
- codec:
libx264 - preset:
veryslow - quality:
crf 10 - pixel format:
yuv420p
WebP defaults:
- frame rate:
30 fps - width:
320 px - quality:
90
MP4 default:
- final encoded size:
1920x1080 - source frames: rendered internally at
3840x2160and downscaled inffmpeg
Check everything:
cargo check --binsRun the scenes:
cargo run --bin rotating_terminal
cargo run --bin stationary_terminal
cargo run --bin intermission
cargo run --bin shutdown_outroRender looping MP4s:
./scripts/render_rotation_loop.sh
./scripts/render_zoom_in.sh
./scripts/render_intermission_loop.sh
./scripts/render_outro_loop.sh
./scripts/render_all_media.sh- Cargo.toml: package metadata and dependencies
- src/lib.rs: shared scene setup, TUI rendering, camera motion, material tuning, and export logic
- src/bin/rotating_terminal.rs: orbit scene
- src/bin/stationary_terminal.rs: zoom-in intro scene
- src/bin/intermission.rs: intermission scene
- src/bin/shutdown_outro.rs: shutdown/outro scene
- src/bin/export_rotation.rs: orbit exporter
- src/bin/export_zoom.rs: zoom-in intro exporter
- src/bin/export_intermission.rs: intermission exporter
- src/bin/export_shutdown.rs: shutdown exporter
- scripts/render_rotation_loop.sh: orbit render helper
- scripts/render_zoom_in.sh: zoom render helper
- scripts/render_intermission_loop.sh: intermission render helper
- scripts/render_outro_loop.sh: shutdown render helper
- scripts/render_scene_media.sh: shared MP4 + WebP render helper used by the scene scripts
- scripts/render_all_media.sh: batch wrapper around the four scene render helpers
- vintage_terminal/: GLTF model and source textures
- bevy_cube_colors/: separate Bevy scratch/example app in the repo
- The terminal screen is a
14x7soft_ratatuisurface composited back into the model's screen texture atlas. - TUI content updates at
15 fps; the live scene runs at30 fps; exports render at60 fps. - The export/live TUI clock advances using the accumulated render interval so intermission and shutdown text timing matches the final renders.
- The terminal body materials are intentionally matte and bloom is kept low to avoid blown-out highlights on the shell.
- Exporters write PNG sequences first, which makes re-encoding with different
ffmpegsettings straightforward.



