A high-performance particle simulator and renderer written in Rust. It
simulates particles moving inside a parametric superellipse-like boundary,
accumulates a 2D density histogram per frame, rasterizes frames to PNG, and
streams them to ffmpeg (image2pipe) to encode MP4 video. Runs are resumable
and metadata for each run is stored in mp4/<index>/meta.json.
- Simulation inside an implicit parametric boundary with analytic reflections at the boundary.
- Per-frame particle positions are accumulated into a 2D histogram and mapped to a spectral palette for rendering.
- PNG frames are produced on worker threads and streamed to
ffmpegvia stdin;ffmpegwritesmp4/<index>.mp4. - Resumable runs:
mp4/<index>/meta.jsontrackslast_frame,constants, timing, etc. - Parallelized with Rayon and uses
mimallocas the global allocator.
- Language: Rust (edition 2024)
- Package manager/build: Cargo
- Key dependencies (see
Cargo.toml):rayon,crossbeam-channel,image,indicatif,serde/serde_json,chrono,ahash,mimalloc - External tools:
ffmpeg(required at runtime for encoding) - Entry point:
src/main.rs
- Rust toolchain via
rustup(stable is fine) ffmpegavailable on PATH (ffmpeg -versionto verify)- Optional (for developer script): Python 3.8+ to run
scripts/inline_sweeper.py
# Build optimized binary
cargo build --releaseOn start, the program asks for a video index:
- Press Enter to use the next available index (creates
mp4/<index>/framesand writesmp4/<index>.mp4). - Or type a numeric index to use/resume an existing directory.
Examples:
- Default run (interactive):
cargo run --release
- Quick test (fewer particles, lower res):
cargo run --release -- \ --n-particles 2000 --res 128 --duration-s 4 --fps 24 --steps-per-frame 50
- Higher quality render:
cargo run --release -- \ --n-particles 20000 \ --res 512 \ --duration-s 20 \ --fps 60 \ --steps-per-frame 150
Frames are streamed to ffmpeg via stdin; the final MP4 is at
mp4/<index>.mp4.
The binary uses clap and exposes these options; defaults reflect the current
src/main.rs constants at the time of writing.
--a <f32>(default: 1.0)--b <f32>(default: 1.0)--n-exp <f32>(default: 2.0)--m-exp <f32>(default: 2.0)--n-particles <u64>(default: 1000)--dt <f32>(default: 0.0001)--epsilon <f32>(default: 1e-8)--center-x <f32>(default: 0.1)--center-y <f32>(default: -0.1)--radius <f32>(default: 0.1)--vx0 <f32>(default: 1.0)--vy0 <f32>(default: 0.0)--fps <u64>(default: 60)--duration-s <u64>(default: 10)--steps-per-frame <u64>(default: 300)--res <u32>(default: 932) — histogram bins per dimension--dpi <u32>(default: 300) — output DPI; output image size (px) =FIG_INCHES * dpi(withFIG_INCHES = 8.0)--sim-threads <usize>(optional) — override auto-detected simulation thread pool size--render-threads <usize>(optional) — override auto-detected render thread pool size--video-filename <string>(optional) — stored inmeta.json; output MP4 path ismp4/<index>.mp4
- Metadata at
mp4/<index>/meta.jsonincludes:constants— effective run parametersdate— run start timelast_frame— last completed frame index (used to resume)compute_time— cumulative compute time (seconds)resolution— histogram resolution (res)
- When resuming, state is advanced by
last_frame * steps_per_framebefore new frames are generated. - Output tree:
mp4/<index>/frames/— optional saved PNGs (useful for debugging)meta.json— run metadata
<index>.mp4— final encoded video
scripts/inline_sweeper.py— experiments with LLVM inlining thresholds by editing.cargo/config.toml, then running the program repeatedly and logging timings.- Requirements: Python 3.8+, Cargo,
ffmpegon PATH. - It will temporarily modify
.cargo/config.tomland restore a backup when done. InspectSTART/END/STEP/REPEATSconstants at the top of the script. - Usage (from repo root):
python3 scripts/inline_sweeper.py
- Requirements: Python 3.8+, Cargo,
RUST_BACKTRACE=1— useful for debugging; the script enables it during runs.- CPU thread detection is automatic; override via
--sim-threads/--render-threadsflags.
- No Rust tests are currently present. TODO: add unit tests/benchmarks for core simulation and rendering components.
- Dedicated Rayon thread pools for simulation and rendering (sized from CPU count) to improve locality and throughput.
- Per-thread buffers reduce contention and are merged after accumulation.
- For faster iterations: reduce
--n-particles,--res,--steps-per-frame, or--fps/--duration-s. - Build with
--releasefor best performance. - Global allocator:
mimalloc. - Additional per-target optimization flags may be set in
.cargo/config.toml(see that file for current values).
- ffmpeg errors / missing: ensure
ffmpegis installed and on PATH (ffmpeg -version). - Permission errors writing to
mp4/: ensure the process can write to the repo directory. - Out-of-memory or crashes: reduce particles, resolution, steps-per-frame, or thread counts.
- Encoder issues: check
ffmpegstderr; run without reduced log-levels to capture more detail.
ParticleAnimatorRust/
├─ Cargo.toml
├─ Cargo.lock
├─ src/
│ └─ main.rs
├─ scripts/
│ └─ inline_sweeper.py
├─ .cargo/
│ └─ config.toml
├─ mp4/ # run outputs: per-index subdirs and final MP4s
├─ target/ # Cargo build artifacts
├─ rustfmt.toml
├─ todo.txt
└─ README.md