A minimal, native Windows desktop wallpaper manager — built with WPF on .NET 8, Magick.NET for decoding every common image format (including WebP), and a clean MVVM architecture. The interface is a restrained grey/black/white "Slate" design in the spirit of the Zen browser and the Zed editor: monochrome chrome, hairline borders, and the only colour on screen coming from your images.
Bibo also sets live video wallpapers (mp4/mov/webm/mkv/avi/wmv) — looping, muted, and rendered straight onto the desktop with libVLC (hardware-decoded, and auto-paused while a full-screen game is in front so it costs no FPS) in the style of the Lively app.
The visual design was explored as four competing mockups and chosen by an independent critique pass — see design/mockups/DESIGN-DECISION.md.
- Import via a multi-select file picker and drag-and-drop onto the window
(folders are accepted and scanned recursively).
Accepted images:
jpg,jpeg,png,gif,bmp,webp,tif,tiff. Accepted video:mp4,m4v,mov,webm,mkv,avi,wmv. Unsupported or corrupt files are skipped with a clear, counted message. - Gallery — a scrollable grid of thumbnails with a Large / Comfort / Compact density toggle. Thumbnails are decoded with Magick.NET, downscaled, and cached on disk, so a library of hundreds of images stays fast across sessions. Filename + resolution show on hover.
- Preview — click a thumbnail to open a docked preview with the full image and its metadata (dimensions, file size, format).
- Set as wallpaper — converts the selected image to a 24-bit BMP under
%AppData%\Bibo\current_wallpaper, writes the fit-mode registry values (WallpaperStyle/TileWallpaper), and applies it via the Win32SystemParametersInfocall (the return value is checked). Fit modes: Fill / Fit / Stretch / Tile / Center. - Live video wallpaper — a video tile shows a poster frame with a ▶ duration badge, and
selecting it offers Set as live wallpaper. A lightweight host process plays the looping,
muted clip on the desktop's WorkerW layer — behind your icons — via libVLC, with the fit
mode mapped to libVLC crop/aspect (cover / contain / stretch). It pauses automatically for
full-screen apps and restarts at login (an
HKCU\…\Runentry). A Live indicator in the status bar stops it in one click, and setting a static image turns it off. - Persistence — imported paths and settings are remembered between sessions in
%AppData%\Bibo\library.json. Moved or deleted files show a first-class Missing state with Locate… / Remove recovery instead of crashing. - Responsive UI — all decoding, thumbnailing and I/O run on background threads and marshal back to the UI thread for binding; bitmaps are frozen for safe cross-thread use.
| Concern | Choice |
|---|---|
| Language / runtime | C# on .NET 8 (net8.0-windows, x64) |
| UI | WPF (XAML), MVVM, no third-party MVVM toolkit |
| Image decoding | Magick.NET-Q8-AnyCPU (14.14.0) |
| Video / live wallpaper | LibVLCSharp (3.8.2) + VideoLAN.LibVLC.Windows (3.0.20), rendered on the desktop WorkerW layer |
| Persistence | System.Text.Json (atomic temp-file + move) |
| Wallpaper | Win32 SystemParametersInfo P/Invoke + HKCU\Control Panel\Desktop; live video via a --wallpaper-host process |
- Windows 10 or 11, x64.
- .NET 8 SDK to build and run from source — https://dotnet.microsoft.com/download/dotnet/8.0.
Newer runtime already installed? The project sets
<RollForward>Major</RollForward>, so a framework-dependent run will roll forward onto a newer major runtime (e.g. .NET 10) when the .NET 8 runtime isn't present. The self-contained publish below bundles its own .NET 8 runtime and needs nothing installed on the target machine.
# from the repository root
dotnet run --project src/Bibo/Bibo.csprojBuild only:
dotnet build src/Bibo/Bibo.csproj -c Releasedotnet testUnit tests cover the deterministic logic layer — fit-mode → registry mapping, supported-format detection and image/video classification, video resolution/duration formatting, the JSON library store (round-trip, missing-file, corrupt-file quarantine), and the Magick.NET-backed image service (metadata, validation, thumbnail generation, wallpaper BMP conversion).
Produces one self-contained, single-file x64 Bibo.exe (no .NET install required on the
target machine; native Magick.NET libraries are bundled and extracted on first run):
dotnet publish src/Bibo/Bibo.csproj -p:PublishProfile=SingleFileOutput: src/Bibo/bin/publish/Bibo.exe.
The publish settings live in
src/Bibo/Properties/PublishProfiles/SingleFile.pubxml
(SelfContained, PublishSingleFile, IncludeNativeLibrariesForSelfExtract,
EnableCompressionInSingleFile).
The published
Bibo.exeis already sendable as-is — no install, no admin, no .NET needed on the recipient's machine. Just hand them the file.
The custom themed installer dist/Bibo-Setup.exe deploys Bibo as loose ReadyToRun
files into %LocalAppData%\Programs\Bibo, so the installed app launches instantly —
there's nothing to self-extract at startup (unlike the single packed exe above). It's a
per-user install (no admin) with a Start-menu shortcut, an optional desktop icon, an
Add/Remove-Programs entry, and a silent uninstaller. It also closes any running copy before
upgrading.
Build it with one command:
powershell -ExecutionPolicy Bypass -File installer\build.ps1That script (1) publishes the app as multi-file ReadyToRun
(Folder.pubxml), (2) zips it,
(3) embeds the zip in the installer (installer/BiboInstaller,
a small themed WPF app), and (4) copies dist/Bibo-Setup.exe out.
Measured cold/warm launch: ~2.7 s / ~0.57 s for the installed loose-file build, vs ~3.7 s / ~0.78 s for the single packed exe.
Trade-off: the installed folder is ~187 MB of loose files (ReadyToRun is larger than packed IL) — disk space in exchange for fast, self-extract-free launches. A legacy Inno Setup script is kept as a plain alternative.
SmartScreen: both the standalone exe and the installer are unsigned, so Windows shows "More info → Run anyway" on first launch. Code-sign with an Authenticode certificate to remove it.
Everything lives under %AppData%\Bibo:
| Path | Purpose |
|---|---|
library.json |
Imported image/video paths + settings (schema version, fit mode, thumbnail size, active live-wallpaper path) |
thumbnails\ |
Cached thumbnail PNGs — image thumbnails and video poster frames (keyed by source path + size + last-write time) |
current_wallpaper\ |
The materialized BMP that Windows points at as the static wallpaper |
library.json shape:
{
"version": 1,
"images": [ { "path": "C:\\Users\\me\\Pictures\\sunset.webp", "addedUtc": "2026-06-08T12:00:00+00:00" } ],
"settings": { "fitMode": "Fill", "thumbnailMaxEdge": 320, "videoWallpaperPath": null }
}A live video wallpaper additionally registers
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Bibo Wallpaper so it resumes at login;
stopping it (or applying a static image) removes that entry.
src/Bibo/
├── Bibo.csproj # net8.0-windows, WPF, x64, Magick.NET
├── app.manifest # PerMonitorV2 DPI awareness
├── App.xaml(.cs) # composition root + global error handling
├── MainWindow.xaml(.cs) # the Slate UI + drag/drop + viewport thumbnail loading
├── Views/ # WallpaperHostWindow — the borderless libVLC live-wallpaper host
├── Common/ # ObservableObject, RelayCommand, AsyncRelayCommand
├── Interop/ # SystemParametersInfo, WorkerW desktop attach, shell video thumbnails/props
├── Models/ # FitMode, MediaKind, AppPaths, ImageMetadata, VideoInfo, SupportedFormats, LibraryState
├── Services/ # ImageService, VideoService, ThumbnailCache, WallpaperService, VideoWallpaperService, LibraryStore, DialogService
├── ViewModels/ # MainViewModel, ImageItemViewModel
├── Converters/ # Bool/Null → Visibility
├── Themes/ # Tokens.xaml (palette/type) + Controls.xaml (styles)
└── Properties/PublishProfiles/ # SingleFile.pubxml
tests/Bibo.Tests/ # xUnit tests for the logic + service layer
design/mockups/ # the four explored designs, renders, and the decision record
- MVVM with hand-wired composition — the object graph is small, so
App.OnStartupconstructs the services and theMainViewModeldirectly (no DI container). View models depend on interfaces (IImageService,IThumbnailCache,IWallpaperService,ILibraryStore,IDialogService) so the logic stays unit-testable. - Thumbnails — WPF has no built-in virtualizing wrap panel, so the gallery uses a
WrapPanel(cheap, empty containers) and decodes a tile's thumbnail only when it scrolls into (or near) the viewport. With the on-disk cache this keeps memory bounded and scrolling smooth for hundreds of images. - Resilience — JSON loads tolerate a missing or corrupt file (the bad file is
quarantined as
library.json.corrupt); saves are atomic; the wallpaper call checks its Win32 return value and surfaces failures; a global handler keeps the app alive on unexpected errors. - Live wallpaper host —
Bibo.exe --wallpaper-host "<video>" <Fit>runs the same executable in a headless mode that attaches a borderless libVLC window to the desktop WorkerW layer (handling both the Windows 10 sibling and the Windows 11 child-of-Progman variants) so the video renders behind the icons. The main app starts/stops this process via a named stop event and registers it underHKCU\…\Runfor login persistence; a foreground watcher pauses playback while a full-screen app is in front so it costs no FPS. Video poster frames and properties come from the Windows shell (IShellItemImageFactory/IShellItem2), not from decoding the clip.
