Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
geodesic-wallpaper
An animated wallpaper for Windows users: glowing lines flow over slowly turning 3D shapes (a donut, a knot, a Klein bottle) behind your desktop icons.
Install (Windows 10 or 11)
| How | Command |
|---|---|
| PowerShell one-liner (easiest) | irm https://gitlab.com/mattbusel/geodesic-wallpaper/-/raw/master/install.ps1 | iex |
| Scoop | scoop bucket add mattbusel https://gitlab.com/mattbusel/scoop-bucket; scoop install mattbusel/geodesic-wallpaper |
| Zip download | Latest release: extract, run geodesic-wallpaper.exe |
| cargo-binstall (prebuilt) | cargo binstall geodesic-wallpaper |
| Cargo (build from source) | cargo install geodesic-wallpaper |
The one-liner checks the download's SHA-256, installs to %LOCALAPPDATA%\Programs\geodesic-wallpaper, adds it to your PATH and makes a Start Menu shortcut. It does not start the wallpaper. The exe is unsigned, so SmartScreen may say "unknown publisher": click More info, then Run anyway. Needs a GPU with DirectX 12 or Vulkan. There are no macOS or Linux builds: it is a Win32 desktop wallpaper.
Use it in 3 steps
- Start it. Open Geodesic Wallpaper from the Start Menu, or run
geodesic-wallpaperin a terminal. After a photosensitivity notice (shown at each start; setepilepsy_warning = falseinconfig.tomlto skip it), the animation replaces your desktop background and sits behind your icons. A tray icon appears. - Pick a look. Run
geodesic-wallpaper --preset ocean(alsocosmic,fire,matrix,neon), or openconfig.tomlnext to the exe, changesurface = "torus"to"torus_knot","klein_bottle","pseudosphere"and so on, and save. The running wallpaper reloads the file by itself. - Stop it. Right-click the tray icon and choose Quit. Your normal wallpaper comes back.
Want a still or a clip without touching your desktop? Headless mode renders offscreen, straight to files:
geodesic-wallpaper --headless --output shot.png # one 1920x1080 PNG
geodesic-wallpaper --headless --frames 240 --record frames --record-start 150 --record-every 2
ffmpeg -framerate 15 -i frames/frame_%05d.png -pix_fmt yuv420p clip.mp4
# or write the clip directly: GIF (no ffmpeg), full-colour APNG, or MP4 (pipes to ffmpeg)
geodesic-wallpaper --headless --width 480 --height 270 --frames 120 --record-start 60 --record-every 2 --gif loop.gif --apng loop.png --mp4 clip.mp4
That last command, run for this release on Windows 11:
Wrote 30-frame GIF (480x270, 15 fps) to loop.gif
Wrote 30-frame APNG (480x270, 15 fps) to loop.png
Wrote 30-frame MP4 (480x270, 15 fps) to clip.mp4
The GIF was 425 KB (256 colours per frame), the APNG 2.4 MB (exact colours, plays in every current browser), the MP4 59 KB.
Results
Six of the fourteen surfaces, rendered today by headless mode at 960x540 with the default palette (the GIF above shows four more):
Real output of the recording command (the frames behind the GIF):
> geodesic-wallpaper --headless --frames 240 --record rec_torus --record-start 150 --record-every 2 --width 900 --height 506
Recorded 45 frames (900x506) to rec_torus
Make a video: ffmpeg -framerate 15 -i rec_torus/frame_%05d.png -pix_fmt yuv420p out.mp4
Each line is one particle sliding along the surface under the geodesic equation, integrated with RK4 (Christoffel symbols are exact formulas for the classic surfaces and central finite differences for the Klein bottle, Boy's surface, ellipsoid, torus knot and trefoil); the trail behind it fades by a power law. On a sphere the paths close into great circles, on a torus they wind forever without repeating, and on the pseudosphere neighbours spread apart exponentially.
What it does
- Fourteen surfaces: torus, sphere, saddle, catenoid, helicoid, hyperboloid, hyperbolic paraboloid, ellipsoid, Enneper, Klein bottle, Boy's surface, torus knot, pseudosphere and trefoil tube.
- Behaves like a wallpaper: a borderless window pinned below all apps, so icons stay clickable. Primary monitor, a chosen monitor, or all of them.
- Live config: edit
config.tomland save; the change shows up without a restart. Five presets ship inpresets/. - Offscreen rendering to PNG, PPM, BMP or SVG, numbered PNG frames for videos, or a looping animated GIF, with no window at all.
- A Rust library too: the surfaces and the RK4 integrator work on their own (docs.rs).
All fields are optional. Missing fields revert to the defaults shown.
Place config.toml in the same directory as the executable. The file is hot-reloaded automatically whenever it changes on disk, no restart required.
| Field | Type | Default | Description |
|---|---|---|---|
surface |
string | "torus" |
Surface to render. See Supported surfaces. |
num_geodesics |
integer | 30 |
Number of simultaneous geodesic curves. |
trail_length |
integer | 300 |
Frames a trail persists before respawning. |
rotation_speed |
float | 0.001047 |
Camera orbit speed in radians per second. |
color_palette |
string[] | 5 entries | CSS hex colour strings cycled across geodesics. |
torus_R |
float | 2.0 |
Torus major radius (centre to tube centre). |
torus_r |
float | 0.7 |
Torus minor radius (tube radius). |
time_step |
float | 0.016 |
RK4 integration timestep in seconds per frame. |
catenoid_c |
float | 1.0 |
Catenoid scale parameter. |
helicoid_c |
float | 1.0 |
Helicoid pitch parameter. |
hyperboloid_a |
float | 1.0 |
Hyperboloid semi-axis a. |
hyperboloid_b |
float | 1.0 |
Hyperboloid semi-axis b. |
ellipsoid_a |
float | 2.0 |
Ellipsoid semi-axis along x. |
ellipsoid_b |
float | 1.5 |
Ellipsoid semi-axis along y. |
ellipsoid_c |
float | 1.0 |
Ellipsoid semi-axis along z. |
hyperbolic_paraboloid_a |
float | 1.0 |
Saddle+ semi-axis a. |
hyperbolic_paraboloid_b |
float | 1.0 |
Saddle+ semi-axis b. |
camera_distance |
float | 6.0 |
Camera distance from origin. |
camera_elevation |
float | 0.4 |
Camera elevation in radians. |
camera_fov |
float | 0.8 |
Vertical field-of-view in radians. |
show_wireframe |
bool | true |
Render surface wireframe mesh. |
trail_fade_power |
float | 2.0 |
Exponent for trail alpha fade (1=linear, 2=quadratic). |
target_fps |
integer | 30 |
Target frame rate. |
background_color |
string | "#050510" |
Background clear colour as CSS hex. |
color_mode |
string | "cycle" |
"cycle" or "random" colour assignment. |
monitor |
string | "primary" |
"primary", "all" (span every monitor) or a monitor index such as "1". |
Minimal example:
= "torus"
= 30
= 300
= 0.001047
= "#050510"
= ["#4488FF", "#88DDFF", "#FFD700", "#88FF88", "#FF88CC"]
config.toml and presets/ are read from the current folder first, then from the folder that holds the exe.
| Flag | Effect |
|---|---|
--preset NAME |
Merge presets/NAME.toml over config.toml |
--headless [--frames N] [--output FILE] [--output-format png|ppm|bmp|svg] |
Simulate N frames offscreen (no window) and save the last one |
--record DIR [--record-start N] [--record-every N] |
With --headless: also save frames as DIR/frame_00000.png, ... |
--gif FILE |
With --headless: write the recorded frames (same --record-start / --record-every schedule) as a looping GIF at 30 / every fps |
--apng FILE |
Same, as a looping animated PNG (full colour) |
--mp4 FILE [--ffmpeg PATH] |
Same, as an H.264 MP4 by piping frames to ffmpeg (install it separately, for example winget install Gyan.FFmpeg) |
--completions SHELL |
Print a completion script for bash, zsh, fish, powershell or elvish |
--width N --height N |
Size of headless renders (default 1920x1080) |
--preview |
Print an ASCII block preview of the pattern and exit |
--animate [--frames N] [--fps N] [--out-dir DIR] |
Exercise the frame exporter; writes gradient test frames, not wallpaper renders |
--palette TYPE[:HUE] [--palette-steps N] |
Print a generated palette (rainbow, monochromatic, complementary, triadic, analogous) |
--gradient, --colorspace, --fractal, --tile |
Print diagnostics from the gradient, color space, fractal and tiling modules, then exit |
--version, --help |
Version, and help with examples |
Set NO_COLOR=1 for plain log output and RUST_LOG=debug for more detail.
| Key | Action |
|---|---|
] / [ |
Next / previous surface |
+ / - |
Speed up / slow down |
R |
Reset all geodesics |
F |
Toggle FPS overlay |
Space |
Pause |
P |
Save a screenshot |
The wallpaper window is created so that it never steals focus, so the tray menu (right-click the tray icon) is the dependable way to switch surface or quit.
All surfaces implement the Surface trait: position(), normal(), metric(), christoffel(), wrap(), random_position(), random_tangent(), and mesh_vertices().
| Surface | Config name | Curvature | Notes |
||-|--|-|
| Torus | "torus" | Mixed | Analytic Christoffels; ergodic irrational windings |
| Sphere | "sphere" | Constant positive K = 1/R² | All geodesics are great circles |
| Saddle | "saddle" | Zero (flat chart) | Straight-line geodesics |
| Catenoid | "catenoid" | Negative (minimal) | Geodesics spiral around the waist |
| Helicoid | "helicoid" | Negative (minimal) | Isometric to catenoid |
| Hyperboloid | "hyperboloid" | Negative | One-sheeted ruled quadric |
| Hyperbolic paraboloid | "hyperbolic_paraboloid" | Negative | Doubly ruled; z = u²/a² − v²/b² |
| Ellipsoid | "ellipsoid" | Positive (varying) | Three independent semi-axes |
| Enneper | "enneper" | Negative (minimal) | Complete; total curvature −4π; self-intersects |
| Klein bottle | "klein_bottle" | Non-orientable | Figure-8 immersion in ℝ³ |
| Boy's surface | "boy_surface" | Non-orientable | RP² with 3-fold symmetry (Apery form) |
| Torus knot | "torus_knot" | Positive (tube) | Default T(2,3) trefoil |
| Pseudosphere | "pseudosphere" | Constant negative K = −1 | Tractricoid; geodesics diverge exponentially; the hyperbolic plane's classic model surface |
| Trefoil tube | "trefoil" | Positive (tube) | Circular cross-section swept around the trefoil knot curve; geodesics precess across all three lobes |
Geodesic equations
On a Riemannian surface with metric g_{ij} a geodesic γ(t) satisfies:
d²uⁱ/dt² + Γⁱⱼₖ (duʲ/dt)(duᵏ/dt) = 0
where Γⁱⱼₖ = ½ gⁱˡ (∂ⱼgₗₖ + ∂ₖgₗⱼ − ∂ₗgⱼₖ) are the Christoffel symbols of the second kind. The torus, sphere, saddle, catenoid, helicoid, hyperboloids, Enneper surface and pseudosphere implement christoffel() with exact formulas. The Klein bottle, Boy's surface, ellipsoid, torus knot and trefoil tube use central finite differences of the metric.
Curvature comparison
| Surface | Gaussian curvature K | Geodesic character | |||-| | Sphere | K = +1/R² (constant) | Great circles: all geodesics are closed | | Torus | Mixed (positive outer, negative inner) | Depends on winding ratio: rational = periodic, irrational = dense (ergodic) | | Saddle / flat | K = 0 | Straight lines in parameter space | | Catenoid / helicoid | K < 0 (minimal) | Geodesics spiral and diverge | | Pseudosphere | K = −1 (constant) | Maximal divergence: model of the hyperbolic plane | | Hyperboloid | K < 0 | Asymptotic geodesics along the rulings |
Gauss-Bonnet theorem
For any compact surface Σ without boundary:
∬_Σ K dA = 2π χ(Σ)
where χ is the Euler characteristic. This connects the local curvature of each built-in surface to its global topology (sphere: χ=2, torus: χ=0, Klein bottle: χ=0, RP²: χ=1).
| Module | Responsibility |
|---|---|
surface/ |
Surface trait and the fourteen implementations, plus procedural and user-defined surfaces |
geodesic |
RK4 integrator for the geodesic ODE |
trail |
Fixed-capacity ring buffer with power-law alpha fade |
renderer/ |
wgpu pipelines and WGSL shaders for the surface wireframe and trails, camera |
wallpaper |
Win32 window pinned below app windows, keyboard input, monitor enumeration |
config |
config.toml loading, profiles and hot reload |
tray |
System tray icon |
main |
CLI, message loop, render loop, headless path |
The crate is also a library (geodesic_wallpaper) with a large set of additional modules that are unit tested but not yet wired into the wallpaper binary: gallery mode, a live parameter tuner, phase portrait recording, mouse-driven geodesic shooting, a geodesic field (basin) view, per-monitor surface assignment, a financial OHLCV data driver, Lua-scripted surfaces (--features lua), scene presets, and generative-art modules (wallpaper symmetry groups, Penrose and Escher tilings, reaction-diffusion, L-systems, strange attractors, Voronoi, fractals, color spaces and export formats). Their config.toml keys (for example gallery_mode) parse but currently have no effect on the running wallpaper.
Use it as a library
Follow one geodesic around a torus (this is examples/follow_geodesic.rs; run it with cargo run --example follow_geodesic):
use Geodesic;
use ;
More examples, none of which open a window:
| Example | What it shows |
|---|---|
cargo run --example follow_geodesic |
The snippet above |
cargo run --example custom_surface |
A surface from a typed formula (sin(sqrt(x^2 + y^2) * 3) * ...), and what bad formulas return |
cargo run --example trails_to_gif |
Twelve geodesics on a torus drawn on the CPU and saved as a GIF and an APNG |
Cargo features
| Feature | Default | What it adds |
|---|---|---|
lua |
off | lua_surface::LuaSurface: define a surface's metric in a Lua script (Lua 5.4 is compiled in via mlua, so a C compiler is needed) |
palette |
off | From conversions between this crate's Rgb, Lab and Oklab and the palette crate's types |
Already using palette? Turn on the feature and convert either way:
let ours: Rgb = new.into;
let back: Srgb = ours.into;
The hand-written Lab and Oklab conversions are checked against palette in the test suite (4,913 colours; Oklab within 0.001, Lab within 0.05).
Performance
cargo bench --bench simulation times the CPU work of one frame (30 geodesics, 300-point trails: RK4 step, trail update, flatten for upload). Same bench file, built against 1.5.1 and 1.6.0 on an i7-13700KF:
| Surface | 1.5.1 | 1.6.0 |
|---|---|---|
| torus | 110.9 µs | 28.4 µs |
| sphere | 105.1 µs | 29.8 µs |
| pseudosphere | 122.1 µs | 40.4 µs |
| Klein bottle | 193.3 µs | 106.7 µs |
The gain is in trail flattening (no modulo or powf per vertex, no allocation per trail); the RK4 step itself is unchanged. A whole --headless run (1280x720, 300 frames, 50 PNGs) takes the same time (about 0.75 s, mostly GPU start-up and PNG encoding) and the same peak memory (243 MB) in both versions, and writes byte-identical images.
Why this and not ...
- Wallpaper Engine / Lively: full wallpaper platforms with workshops of content. geodesic-wallpaper is one free, open-source program that generates its own animation from maths; there is nothing to download or subscribe to.
- bevy_live_wallpaper: a Bevy plugin for building your own live wallpaper. Use it if you are writing a Bevy app; use this if you want a finished wallpaper, or geodesic maths as a small library.
Build from source
git clone https://gitlab.com/mattbusel/geodesic-wallpaper.git
cd geodesic-wallpaper
cargo run --release # the live wallpaper, reads ./config.toml
cargo run --release -- --headless --output shot.png
cargo test --lib # no GPU needed
Contributing: CONTRIBUTING.md. Changes: CHANGELOG.md.
License
MIT, see LICENSE.