Skip to content

shinkuan/Waifuland

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Waifuland

Disclaimer: This repository is "vibe coded". Please use with caution.

A Linux desktop Live2D model viewer that renders Live2D characters as transparent Wayland overlay windows using the wlr-layer-shell protocol.

Live2D models float on your desktop with click-through transparency — only the model itself receives input. Built with the Live2D Cubism SDK for Native, OpenGL (EGL), and native Wayland (no X11, no GLFW windowing).

compressed.mp4

Table of Contents

Features

  • Wayland-native overlay — renders as a layer-shell surface, always on top of your desktop
  • Click-through transparency — only the Live2D model area receives pointer events; everything else passes through
  • Multi-compositor support — works on any Wayland compositor supporting wlr-layer-shell (Hyprland, Sway, river, etc.)
  • Multi-output support — switch between monitors (Hyprland)
  • Interactive — responds to mouse drag, tap, and scroll input
  • Motion & expression — supports idle animations, lip-sync, eye-blink, physics, and expressions
  • Configurable model directory — load models from any path via CLI flag or XDG config
  • JSON configuration — customize behavior via config.json (default model, emotion timeout, additional model dirs, scale/position, window size)
  • IPC control — Unix domain socket API for external control (model switching, expressions, motions, lipsync, zoom, position, look-at direction)

Prerequisites

Dependency Notes
Linux with Wayland compositor Must support wlr-layer-shell-v1 (Hyprland, Sway, river, etc.)
Live2D Cubism SDK for Native Download from live2d.com/sdk (proprietary, not bundled)
C++ compiler GCC or Clang with C++14 support
CMake >= 3.16
pkg-config For finding Wayland/EGL libraries
Wayland development libraries wayland-client, wayland-egl, wayland-cursor
EGL & OpenGL libegl-dev, libgl-dev or equivalent
wayland-scanner Usually part of wayland-protocols or wayland dev packages
curl, unzip For downloading third-party dependencies (GLEW)

Installing system dependencies

Arch Linux:

sudo pacman -S --needed base-devel cmake pkgconf wayland wayland-protocols libglvnd egl-wayland curl unzip

Ubuntu / Debian:

sudo apt install build-essential cmake pkg-config libwayland-dev wayland-protocols libegl-dev libgl-dev curl unzip

Fedora:

sudo dnf install gcc-c++ cmake pkgconf-pkg-config wayland-devel wayland-protocols-devel mesa-libEGL-devel mesa-libGL-devel curl unzip

Installation

1. Clone the repository

git clone https://github.com/shinkuan/waifuland.git
cd waifuland

2. Download the Live2D Cubism SDK

  1. Go to https://www.live2d.com/en/sdk/about/
  2. Download Cubism SDK for Native
  3. Extract it into the project root directory:
    waifuland/
    ├── CubismSdkForNative-5-r.5/   <-- extracted SDK here
    ├── CMakeLists.txt
    ├── src/
    └── ...
    
  4. If the folder name differs from CubismSdkForNative-5-r.5, update SDK_ROOT_PATH in CMakeLists.txt.

3. Build

Use the install script (recommended):

chmod +x install.sh
./install.sh

Or build manually:

cd thirdParty && bash scripts/setup_glew && cd ..
mkdir -p build && cd build
cmake ..
make -j$(nproc)

The binary will be at build/bin/waifuland.

Usage

# Run with default model directory (~/.config/waifuland/models/)
./build/bin/waifuland

# Run with a custom model directory
./build/bin/waifuland --models_dir /path/to/your/models

# Run with a custom config file
./build/bin/waifuland --config /path/to/config.json

Configuration

Waifuland reads a JSON config file from $XDG_CONFIG_HOME/waifuland/config.json (fallback: ~/.config/waifuland/config.json). Override the path with --config <path>.

Example config.json:

{
    "additional_model_dirs": [
        "/home/user/extra-models",
        "/opt/shared-models"
    ],
    "default_model": "MyFavoriteModel",
    "emotion_timeout": 5,
    "model_scale": 1.0,
    "model_x": 0.0,
    "model_y": 0.0,
    "window_width": 1900,
    "window_height": 1000
}
Key Type Default Description
additional_model_dirs string[] [] Extra directories to scan for models (in addition to the default models dir)
default_model string "" Name of the model subfolder to load first on startup
emotion_timeout float 5 Seconds before expression reverts to default. Set to -1 to never revert
model_scale float 1.0 Initial model scale
model_x float 0.0 Initial model X offset
model_y float 0.0 Initial model Y offset
window_width int 1900 Render target width in pixels
window_height int 1000 Render target height in pixels

All fields are optional. Missing fields use their default values. If the config file doesn't exist, all defaults are used.

Controls

Input Action
Left-click Upper Body Trigger expression change (if any)
Left-click Lower Body Trigger motion (if any)
Left-click drag Drag the model / trigger hit areas
Right-click Switch to next model
Middle-click Switch skin (if any)
Scroll wheel Zoom in/out

Compositor Compatibility

Waifuland runs on any Wayland compositor that supports the wlr-layer-shell protocol. Some features require compositor-specific IPC and are only available on Hyprland:

Feature Hyprland Sway Other wlroots
Overlay rendering Yes Yes Yes
Click-through transparency Yes Yes Yes
Mouse drag / tap / scroll Yes Yes Yes
Global cursor tracking (eyes follow cursor anywhere) Yes No No
Cross-monitor drag Yes No No
Move to focused monitor Yes Yes No

The compositor is auto-detected at startup. Feature availability is logged to the console.

Toggling Visibility

You can hide and show your Live2D model (along with its click-through input region) by sending a SIGUSR1 signal to the background process. When hidden, it uses virtually no resources.

kill -SIGUSR1 $(pgrep -x waifuland)
# Or
killall -s SIGUSR1 waifuland
# Or using the included toggle command:
./build/bin/waifuland toggle

Move to Focused Monitor

Move the model to whichever monitor currently has focus by sending a SIGUSR2 signal. This works on Hyprland and Sway.

kill -SIGUSR2 $(pgrep -x waifuland)
# Or
killall -s SIGUSR2 waifuland
# Or using the included focus command:
./build/bin/waifuland focus

Hyprland keybind examples
Add these to your ~/.config/hypr/hyprland.conf:

# Toggle waifuland visibility with Super + W
bind = SUPER, W, exec, killall -s SIGUSR1 waifuland
# Move waifuland to focused monitor with Super + Shift + W
bind = SUPER SHIFT, W, exec, killall -s SIGUSR2 waifuland

IPC API

Waifuland exposes a Unix domain socket for external control by scripts and programs.

  • Socket path: $XDG_RUNTIME_DIR/waifuland.sock (fallback: /tmp/waifuland.sock)
  • Protocol: Newline-delimited JSON — send {"command":"<name>", ...}\n, receive {"ok":true, ...}\n
  • Requires: socat (install via your package manager)

CLI Client

A convenience CLI client waifuland-ctl is included in the project root:

./waifuland-ctl <command> [--key value ...]

Available Commands

get_status — Get general status

Returns current model, zoom, position, and visibility.

./waifuland-ctl get_status
{
    "ok": true,
    "model": "MyModel",
    "model_index": 0,
    "model_count": 3,
    "hidden": false,
    "zoom": 1.0,
    "x": 0.0,
    "y": 0.0
}

get_available_models — List all discovered models

./waifuland-ctl get_available_models
{
    "ok": true,
    "models": ["MyModel", "AnotherModel", "ThirdModel"]
}

get_current_model — Get current model name and index

./waifuland-ctl get_current_model
{
    "ok": true,
    "model": "MyModel",
    "index": 0
}

set_model — Switch model by name or index

# By name
./waifuland-ctl set_model --name "AnotherModel"

# By index
./waifuland-ctl set_model --index 2
{
    "ok": true
}

next_model — Switch to next model

./waifuland-ctl next_model
{
    "ok": true
}

prev_model — Switch to previous model

./waifuland-ctl prev_model
{
    "ok": true
}

get_expressions — List expressions for current model

./waifuland-ctl get_expressions
{
    "ok": true,
    "expressions": ["happy.exp3.json", "angry.exp3.json", "sad.exp3.json"]
}

set_expression — Set expression by ID

./waifuland-ctl set_expression --id "happy.exp3.json"
{
    "ok": true
}

get_motions — List motions for current model

./waifuland-ctl get_motions
{
    "ok": true,
    "motions": [
        {"group": "Idle", "index": 0, "file": "idle_01.motion3.json"},
        {"group": "TapBody", "index": 0, "file": "tap_01.motion3.json"}
    ]
}

do_motion — Play a motion

# Play a specific motion by group and index
./waifuland-ctl do_motion --group "TapBody" --index 0

# Play with custom priority (default: 2 = Normal)
./waifuland-ctl do_motion --group "Idle" --index 0 --priority 3

# Play a random TapBody motion (omit group)
./waifuland-ctl do_motion
{
    "ok": true
}

set_mouth_y — Set mouth opening for external lipsync

Value range: 0.0 (closed) to 1.0 (fully open). Send continuously for real-time lipsync.

./waifuland-ctl set_mouth_y --value 0.8
{
    "ok": true
}

set_model_zoom — Set model zoom/scale

Value range: 0.1 to 10.0.

./waifuland-ctl set_model_zoom --value 1.5
{
    "ok": true
}

set_model_position — Set model position offset

./waifuland-ctl set_model_position --x 0.5 --y -0.3
{
    "ok": true
}

get_model_position — Get current position and zoom

./waifuland-ctl get_model_position
{
    "ok": true,
    "x": 0.5,
    "y": -0.3,
    "zoom": 1.5
}

toggle_hidden — Toggle window visibility

./waifuland-ctl toggle_hidden
{
    "ok": true,
    "hidden": true
}

switch_skin — Switch model skin/outfit

./waifuland-ctl switch_skin
{
    "ok": true
}

set_look — Override look-at direction (for head/face tracking)

# Set look direction (x, y range: -1.0 to 1.0)
./waifuland-ctl set_look --x 0.5 --y 0.3

# Reset to default (follow cursor)
./waifuland-ctl set_look --reset
{
    "ok": true
}

Raw Socket Usage

You can also communicate directly with the socket without waifuland-ctl:

# Using socat
echo '{"command":"get_status"}' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/waifuland.sock

# Using Python
python3 -c "
import socket, json
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('$XDG_RUNTIME_DIR/waifuland.sock')
sock.send(b'{\"command\":\"get_status\"}\n')
print(sock.recv(4096).decode())
sock.close()
"

Model Setup

Place Live2D models in your models directory. Each model should be in its own subfolder containing a .model3.json file:

~/.config/waifuland/models/
├── MyModel/
│   ├── MyModel.model3.json
│   ├── MyModel.moc3
│   ├── textures/
│   └── motions/
└── AnotherModel/
    └── ...

The default models directory is $XDG_CONFIG_HOME/waifuland/models/ (fallback: ~/.config/waifuland/models/). Override it with the --models_dir flag.

Architecture

waifuland/
├── src/                    # Application source code
│   ├── main.cpp            # Entry point, CLI argument parsing
│   ├── LAppConfig.*        # JSON config file reader (singleton)
│   ├── LAppWayland.*       # Wayland client setup (display, compositor, EGL, layer-shell)
│   ├── LAppWaylandRegion.* # Input region management (click-through transparency)
│   ├── LAppDelegate.*      # Main app controller, render loop, input handling
│   ├── LAppLive2DManager.* # Model lifecycle management
│   ├── LAppView.*          # View/projection matrices, rendering coordination
│   ├── LAppModel.*         # Individual Live2D model instance
│   ├── LAppIPC.*           # IPC socket server for external control
│   └── LAppDefine.*        # Global constants and configuration
├── protocol/               # Wayland protocol XML files
│   ├── xdg-shell.xml
│   └── wlr-layer-shell-unstable-v1.xml
├── thirdParty/             # Third-party dependencies (GLEW, stb)
├── cmake/                  # CMake helper scripts
└── CMakeLists.txt          # Build configuration

Key design decisions

  • No GLFW windowing — Wayland surfaces are created directly via wl_compositor and zwlr_layer_shell_v1 for overlay behavior that GLFW cannot provide.
  • EGL rendering — OpenGL context is managed through EGL, bound directly to the Wayland display.
  • Layer-shell overlay — the application renders as a Wayland layer surface, sitting above normal windows.
  • Input region masking — only the model's bounding area accepts input; the rest of the surface is fully transparent and click-through.

Built With

License

This project includes code adapted from the Live2D Cubism SDK samples, which are subject to the Live2D Open Software License.

The Live2D Cubism SDK (Core library) is proprietary and must be downloaded separately. See Live2D SDK License.

About

A Linux desktop Live2D model viewer that renders characters as transparent Wayland overlay windows using wlr-layer-shell.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages