Rust bindings and viewer for Live2D Cubism SDK for Native.
Supports Cubism 5.x (V3) and Cubism 2.x (V2) models in a single application.
| Crate | Description |
|---|---|
live2d-core-sys |
Raw -sys FFI declarations generated by bindgen over the Cubism 5.x Core C API (csmMoc, csmModel, etc.) |
live2d-core |
Safe Rust wrapper for V3 — Moc, Model, Parameters, Parts, Drawables, CanvasInfo, OffscreenInfos |
live2d-v2-core-sys |
bindgen FFI for Cubism 2.x C API (built from live2d-py C++ artifacts) |
live2d-v2-core |
Safe Rust wrapper for V2 — model loading, parameter control, rendering |
live2d-viewer |
Desktop viewer binary: winit + glutin + glow (OpenGL 3.3 Core) + egui UI + self-implemented motion system |
live2d-py/ is a separate git submodule (not in the workspace) — its built C++ artifacts feed live2d-v2-core-sys.
- Rust 1.75+ (MSRV, set per crate)
- Cubism 5.x SDK for Native — place at workspace root as
CubismSdkForNative-5-r.5/, or setLIVE2D_SDK_ROOTto point elsewhere - OpenGL 3.3 Core capable GPU (runtime, for viewer)
# 1. Download Cubism SDK for Native from Live2D:
# https://www.live2d.com/download/cubism-sdk/download-native/
# Extract it as CubismSdkForNative-5-r.5/ under the workspace root.
# 2. Build & run with a model directory:
cargo build --release
cargo run --release -p live2d-viewer -- /path/to/model-directoryThe build system statically links libLive2DCubismCore.a from the SDK by default. See Linking for alternatives.
Sample models are bundled in the Cubism SDK under Samples/Resources/:
cargo run --release -p live2d-viewer -- \
/path/to/CubismSdkForNative-5-r.5/Samples/Resources/HaruWhen LIVE2D_SDK_ROOT is set and no model path is given on the command line, the viewer automatically scans $LIVE2D_SDK_ROOT/Samples/Resources/ for all V3 and V2 models:
LIVE2D_SDK_ROOT=/path/to/CubismSdkForNative-5-r.5 cargo run --release -p live2d-viewerA small always-on-top window positioned at the bottom-right corner of the screen:
cargo run --release -p live2d-viewer -- --overlay /path/to/model-directory# Default build (static link)
cargo build --release
# Dynamic linking (load libLive2DCubismCore.so at runtime)
cargo build --release --no-default-features
# Run viewer
cargo run --release -p live2d-viewer -- /path/to/model-dir
# Overlay mode (small window, bottom-right corner)
cargo run --release -p live2d-viewer -- --overlay /path/to/model-dir
# Testing (requires SDK samples available at LIVE2D_SDK_ROOT/Samples/Resources)
LIVE2D_SDK_ROOT=/path/to/CubismSdkForNative-5-r.5 cargo test --release -p live2d-core-sysV2 models require building the C++ wrapper from the live2d-py submodule first:
# Build live2d-py (see its own build instructions)
cd live2d-py && mkdir build && cd build && cmake .. && make
# Set the build output directory and run
V2_PY_BUILD_DIR=/path/to/live2d-py/build cargo run --release -p live2d-viewer -- /path/to/v2-model-dirThe viewer auto-detects model format by checking for *.model3.json (V3) vs *model*.json (V2) in the directory.
- Model list — select, rename (double-click or ✏ button), delete (✖ button), or add new model directories via file picker
- Model history — previously loaded models are persisted in a SQLite database (
~/.local/share/live2d-viewer/) and restored on startup - Scan directories — configure folders to scan recursively for models; generic system names (e.g. engineering code names) are auto-skipped
- Model search — full-text fuzzy search across model names via SQLite FTS5 with similarity scoring
- Model validation — V3 model files are validated (MOC3 + textures existence) before being added to the list; invalid entries are skipped
- Async model switching — V3 model files are loaded on a background thread via
mpscchannel; the main thread drains the receiver without blocking rendering
- OpenGL 3.3 Core renderer with
glowtyped wrapper - 3 shader programs: standard (un-masked drawables), mask (FBO), masked (composite with per-pixel mask)
- Cached uniform locations — queried once at shader init for ~30 fewer driver calls per frame
- Multiply/screen blending support via
uMultiplyColor/uScreenColoruniforms - Mask compositing — mask shapes rendered to offscreen FBO, then sampled in the masked fragment shader using
gl_FragCoord/uMaskSize
- Separate GL 2.1-style rendering path via the C++ wrapper
- Requires a VAO wrapper for core profile compatibility
- V2 code resets GL state (VAO, program, textures, blend) after drawing because the V2 internals leave GL in an unknown state
The motion system is self-implemented (not Cubism Framework), located in live2d-viewer/src/motion/:
- Custom JSON parser — parses Cubism motion3.json files without the official framework
- Curve evaluator — supports all interpolation types (linear, bezier, stepped)
- Queue manager — per-group queues for concurrent independent motions (e.g. idle + expression)
- Eye blink — automatic eyelid closure with configurable interval and duration
- Breath — subtle breathing motion on chest/body parameters
- Look-at-cursor — eye tracking toward mouse position with smooth interpolation
- Expressions — loaded from
expressions/*.exp3.jsonwith fade transitions - Physics — physics3.json simulation with spring dynamics (hair/clothing/swing items)
- Pose — auto-fade between tap motions via pose3.json groups
- Tap detection — click on model body triggers a random "TapBody" motion (V3: from loaded motions; V2: cycles through internal C++ motions)
- Audio — V2 motions can trigger sound playback via
rodio(OGG/WAV/MP3)
- Interactive pan & zoom — drag to pan, scroll or buttons to zoom
- Reset — one-click restore default view
- Layout mode — enter adjustment mode with pan X/Y and zoom sliders for precise positioning
- Per-model layout persistence — pan position + zoom level saved to DB per model, restored on switch (V2 stores
v2_scale; V3 computes from camera with proper Y-flip and aspect ratio)
Three desktop pet modes accessible from the GUI or system tray:
| Mode | Description |
|---|---|
| Off | Normal windowed viewer |
| Windowed Pet | Decorationless always-on-top window sized to model canvas; behaves like a desktop mascot |
| AlwaysOnTop Pet | On Linux/Wayland: spawns a separate smithay-client-toolkit layer-shell surface (bypasses window manager). On X11: similar to Windowed but with floating circle minimize |
Additional pet features:
- Floating circle minimize — minimize to a small draggable overlay circle (saved as 50×50 on Wayland)
- Pet toolbar — raw-GL overlay with buttons (prev/next model, zoom in/out, reset camera, search, exit pet)
- Click-through mode — input passthrough so clicks reach windows underneath
- Tray icon — system tray menu to toggle pet modes, toggle click-through, show/hide window, or quit
The viewer uses egui for all UI overlays:
- Model List panel — select, rename, delete, add model directories
- Parameters panel — all model parameters as sliders with live update
- Search panel — full-text model search with similarity scores
- Settings panel — configure scan directories, view scan results
- Motion status — display current motion queue entries, expression state, fade weights
- Action buttons — replay idle, stop all, tap body
- Zoom controls — zoom in/out/reset buttons
- Pet mode buttons — toggle Windowed Pet / AlwaysOnTop
- Loading indicator — shown during async model switch
- CJK font support — loads
SourceHanSansCN-Medium.otfat startup; without it, Chinese/Japanese labels render as□□□
- System tray — cross-platform tray icon (GTK on Linux via
ksni,tray-iconon other platforms) with full menu - Wayland handling — auto-detects Wayland, forces X11 backend for winit; on Wayland-only compositors, tray degrades gracefully; pet mode respawns the process with appropriate flags
- Floating play button — raw GL triangle overlay avoids an
egui_glowcoordinate bug at small window sizes - TextRenderer — atlas-based monospace text renderer used in the pet overlay (raw GL, not egui)
- Database — SQLite via
libsqlfor model history and settings persistence
┌─────────────────────────────────────────────┐
│ live2d-viewer │ Desktop viewer binary
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ Renderer │ │ GUI │ │ Motion │ │
│ │ (glow) │ │ (egui) │ │ System │ │
│ │ V3/V2 │ │ Pet Tool │ │ Blink/Breath│ │
│ │ Shaders │ │ bar │ │ Look/Phys │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ Camera │ │ Database │ │ Pet/Overlay │ │
│ │ (Zoom) │ │ (SQLite) │ │ (Wayland) │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
├─────────────────────────────────────────────┤
│ live2d-core (safe V3 wrapper) │
│ Moc / Model / Parameters / Drawables │
├─────────────────────────────────────────────┤
│ live2d-v2-core (safe V2 wrapper) │
├──────────────────┬──────────────────────────┤
│ live2d-core-sys │ live2d-v2-core-sys │
│ (bindgen V3 FFI) │ (bindgen V2 FFI) │
├──────────────────┴──────────────────────────┤
│ Cubism Core 5.x (C static lib / .so) │
│ live2d-py (V2 C++ wrapper, submodule) │
└─────────────────────────────────────────────┘
Auto-generated FFI declarations via bindgen. Generates at build time from CubismCore/include/Live2DCubismCore.h.
Key types: csmMoc, csmModel, csmVector2, etc.
Safe zero-cost wrappers with lifetimes for V3 models:
let moc = Moc::from_bytes(&moc3_data)?; // load MOC3 data
let mut model = Model::initialize(&moc)?; // create model instance
let canvas = model.canvas_info(); // pixel size, origin
let params = model.parameters(); // parameter IDs, values, ranges
let parts = model.parts(); // part opacity
let drawables = model.drawables(); // vertex/index data, textures, masks
let orders = model.render_orders(); // draw order
model.update(); // run physics/motionSafe Rust wrapper for V2 models, wrapping the C++ API from live2d-py:
- Model loading via
Model::from_file()/Model::from_bytes() - Parameter get/set, scale and offset control
- Motion triggering and sound path lookup
Full desktop application. See Viewer Features above.
Viewer uses 3 GLSL 330 Core shaders:
| Program | Uniforms | Purpose |
|---|---|---|
program |
uTexture, uMultiplyColor, uScreenColor, uOpacity | Standard un-masked drawables |
mask_program |
uScale, uTranslate, uOpacity | Render mask shapes to FBO |
masked_program |
+uMaskTexture, uMaskSize | Per-pixel mask compositing via gl_FragCoord |
Mask rendering flow:
- Bind offscreen FBO, draw mask geometry → mask alpha in FBO texture
- Switch to masked program, bind FBO texture as
uMaskTexture - Sample mask in fragment shader by computing UV from
gl_FragCoord/uMaskSize - Multiply fragment alpha by mask value
By default, the build uses the static-link feature (enabled via default = ["static-link"] in all crate Cargo.tomls):
- Static (default): links
libLive2DCubismCore.afrom the SDK at build time - Dynamic (
--no-default-features): linkslibLive2DCubismCore.soand adds-rpathto the binary vialive2d-viewer/build.rs
The SDK is located either:
- At
../CubismSdkForNative-5-r.5relative to the workspace root - Via
LIVE2D_SDK_ROOTenvironment variable overriding the path
- Release profile (in root
Cargo.toml):lto=true,strip=true,panic="abort",codegen-units=1,overflow-checks=false. Always build with--releasefor actual use — debug is slow. - V2 core depends on C++ build artifacts from
live2d-py.live2d-v2-core-sys/build.rslinksv2_c_api,V2, andgladas static libs. On Linux it also linksGL,stdc++fs,stdc++,m. bindgenruns at build time for both-syscrates. Generated code lands inOUT_DIR/bindings.rs.
main.rs detects WAYLAND_DISPLAY and forces WINIT_UNIX_BACKEND=x11 + GDK_BACKEND=x11. The tray icon uses GTK (ksni crate), which also needs X11. On Wayland-only compositors, GTK init may fail → tray icon is disabled (graceful fallback).
Minimize-to-floating-circle uses a different code path on X11 (hide) vs Wayland (resize to 50×50 float overlay). AlwaysOnTop pet mode on Wayland spawns a separate smithay-client-toolkit layer-shell thread instead of relying on winit.
All tests are integration-style and require the Cubism SDK:
LIVE2D_SDK_ROOT=/path/to/CubismSdkForNative-5-r.5 cargo test --release -p live2d-core-syslive2d-core-sys/src/lib.rs: unit tests forcsmGetVersion(),csmGetLatestMocVersion(), log function roundtriplive2d-viewer/src/model_loader.rs: model dump tests that load real SDK sample models (Mao, Rice, Natori) — these are debug/exploration tests that print model structure but don't assert much
cargo fmt
cargo clippy
cargo check --releaseOrder: fmt → clippy → test. No custom rustfmt.toml or clippy.toml. Some #[allow(clippy::...)] annotations exist on specific items.
| Status | Feature |
|---|---|
| ✅ | AI chat companion — configurable provider (OpenAI-compatible), per-model session chat, chat panel UI, settings panel UI |
| (✅) | HitAreas + tap-to-motion — hit-testing from model3.json HitAreas (V3) and C++ hitTest (V2); tap triggers body motions. Done. |
| ✅ | UserData (userdata3.json) — parsed into ID→description lookup map; shown in GUI on tap |
| ✅ | Save/Load parameter presets — named snapshots stored in DB per model; restore via GUI |
| ✅ | Layout settings — per-model pan + zoom saved to DB; layout mode with sliders for precise positioning |
This project is not affiliated with Live2D Inc.
live2d-core-syscontains FFI declarations for the Cubism Core SDK, used under Live2D's license terms.- All other code is provided under the MIT License.