A Kodi music visualization addon backed by
Lactea — a pure-Rust MilkDrop 2/3 and
projectM-compatible visualizer built on wgpu.
This is the Lactea counterpart of
visualization.projectm:
instead of wrapping the C++ libprojectM, it links Lactea's C ABI
(lactea-capi)
and renders its frames onto Kodi's surface.
Lactea also ships as a Rust library, a standalone desktop app and a browser demo; see the Lactea README for those, for the fidelity measurements against projectM and for the C API reference.
- Reads the whole
.milkpreset format — custom waves and shapes, per-frame and per-vertex code, warp and composite shaders — with MilkDrop 2, MilkDrop 3 or projectM semantics (auto-detected per preset by default). - Rotates presets automatically from a folder of your choice (or the system projectM preset folder when none is set), with soft cuts, shuffle, optional beat-triggered cuts and a built-in idle preset as the last fallback.
- Reacts to Kodi's audio (interleaved float PCM) and to the preset controls Kodi exposes (next / previous / random / load / lock).
Each release ships
visualization.lactea-<version>.zip for Kodi 21 (Omega) on Linux x86_64
(built on Ubuntu 24.04, so glibc 2.39 or newer), with Lactea linked in
statically. In Kodi, allow Unknown sources (Settings → System → Add-ons),
then Settings → Add-ons → Install from zip file, pick the zip and select
Lactea under Settings → Player → Music → Visualisation. A Vulkan driver
is required at runtime (Mesa's lavapipe works in software). For presets,
install your distribution's projectM preset package or set Preset folder in
the addon settings.
Unlike visualization.projectm, Lactea does not draw into Kodi's OpenGL context.
It owns a wgpu device (Vulkan, Metal or DX12) and renders into an offscreen
target; lactea_render_to_buffer copies each frame back as tight RGBA8. The
addon uploads those pixels into a GL texture and blits a fullscreen quad using a
tiny shader (resources/shaders/GL and GLES). This keeps the integration
portable across Kodi's GL and GLES render paths at the cost of a CPU read-back
per frame. The render height is capped (setting Maximum render height,
1080p by default) and the result is scaled to the screen, so 4K displays do not
read back 33 MB per frame. Window resizes are followed with lactea_resize.
Preset management lives in src/LacteaPlayer.{h,cpp}, a Kodi-independent
wrapper over the Lactea engine and playlist (lactea_playlist_*): a playlist
connected to the engine scans the preset folder, answers the engine's switch
requests and drives automatic rotation. Setting changes apply to the running
engine in place; only the compatibility mode needs a new engine, which is built
once on the next frame and replaces the old one only if creation succeeds. The
lock state survives rebuilds. src/Main.cpp is the thin Kodi/GL layer.
This addon targets Kodi 21 (Omega), which exposes the visualization API as
Start() / Stop() / GetSyncDelay(). Kodi 22+ (master) renamed these to
AudioStart() / AudioStop() / AudioGetSyncDelay() and dropped Init() /
DeInit() in favor of them; targeting master requires renaming those three
overrides. CI builds against the Omega branch.
You need the Kodi dev headers and a Lactea liblactea_capi + lactea.h
(Lactea 0.2.2 or newer). The Linux tarballs on the
Lactea releases page ship both
(lib/liblactea_capi.{so,a}, include/lactea.h). The lactea-capi crate is
not published to crates.io, so without a pre-built library it is compiled from
a Lactea checkout with cargo.
git clone https://github.com/M0Rf30/visualization.lactea.git
cd visualization.lactea && mkdir build && cd build
cmake -DKodi_DIR=/usr/lib/kodi/cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$HOME/.kodi/addons \
-DOVERRIDE_PATHS=1 ..
make installgit clone --branch Omega https://github.com/xbmc/xbmc.git
git clone https://github.com/M0Rf30/visualization.lactea.git
git clone --depth 1 --branch v0.2.2 https://github.com/M0Rf30/lactea.git
# The addon is not in Kodi's binary-addons list; give the framework a definition.
mkdir -p xbmc/cmake/addons/addons/visualization.lactea
echo "visualization.lactea $PWD/visualization.lactea" \
> xbmc/cmake/addons/addons/visualization.lactea/visualization.lactea.txt
echo all > xbmc/cmake/addons/addons/visualization.lactea/platforms.txt
# Kodi's addon framework does not forward -D options to the addon, so pass the
# Lactea checkout through the environment.
export LACTEA_SOURCE_DIR=$PWD/lactea
cmake -B visualization.lactea/build \
-DADDONS_TO_BUILD=visualization.lactea \
-DADDON_SRC_PREFIX=$PWD \
-DADDONS_DEFINITION_DIR=$PWD/xbmc/cmake/addons/addons \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$PWD/xbmc/kodi-build/addons \
-DPACKAGE_ZIP=1 \
xbmc/cmake/addons
make -C visualization.lactea/buildFindlactea.cmake runs cargo build --release -p lactea-capi against
LACTEA_SOURCE_DIR at configure time (a no-op when up to date), or otherwise
looks for a pre-built library and lactea.h (ADDON_DEPENDS_PATH, a standard
library path, or LACTEA_LIBRARY / LACTEA_INCLUDE_DIR).
Linking. A static liblactea_capi.a is preferred and makes the addon
self-contained. With only the shared library, a copy found outside the system
library directories (e.g. a cargo target/release) is installed next to the
addon binary and located through an $ORIGIN rpath; a system-installed copy is
left to the distribution to provide.
Cross-compilation note. Kodi's
dependsframework has nocargotarget, so the Rust build is driven by CMake. Building for Android or Windows needs the corresponding Rust target installed (rustup target add …) and the rightCARGO_TARGET_*toolchain, which the stockdependsflow does not set up. Only Linux desktop builds are exercised.
tests/preset_cycle.cpp drives CLacteaPlayer against a real Lactea engine
with simulated time and audio, and checks automatic rotation order, shuffle,
next / previous / random / load, locking, a broken preset not stalling the
rotation, folder reload, and engine rebuild / resize. It needs a GPU (Vulkan
lavapipe is enough) and exits 77 (skip) if no engine can be created.
cmake -DLACTEA_BUILD_TESTS=ON ... && make && ctest --output-on-failure| setting | effect |
|---|---|
| Preset folder | folder of .milk / .milk2 / .prjm presets (recursive); empty → first existing of /usr/share/projectM/presets, /usr/local/share/projectM/presets, /usr/share/projectm/presets, /usr/share/lactea/presets, ~/.projectM/presets, else the built-in idle preset |
| Texture folder | images presets reference (sampler_*); optional |
| Shuffle mode | pick presets in random order |
| Smooth preset duration | seconds a soft cut blends old and new preset |
| Preset duration | seconds a preset runs before switching |
| Beat-triggered preset changes | let a loud beat cut to the next preset early |
| Beat sensitivity | how loud a beat must be to cut (maps to Lactea's hard_cut_sensitivity, 3.0 … 1.0) |
| Compatibility mode | auto / projectM / MilkDrop 2 / MilkDrop 3 (rebuilds the engine) |
| Maximum render height | cap on the rendered frame height; the frame is scaled to the screen |
GPL-2.0-or-later, see LICENSE.md. Lactea itself is MIT OR Apache-2.0.