No description
  • Rust 98.7%
  • Nix 1.3%
Find a file
municorn 5525fa451d
feat(editor): add view controls panel with auto-fit toggle and zoom slider
Adds a View section to the editor sidebar that exposes ViewConfig fields
directly. The auto-fit checkbox enables or disables automatic camera
scaling, and the logarithmic zoom slider adjusts the zoom multiplier.
Changes take effect immediately without triggering an entity respawn.
2026-04-14 23:21:11 -06:00
.github chore: remove blank line from FUNDING.yml 2026-04-12 13:45:39 -06:00
docs/plans docs: augment feature expansion with Bevy migration 2026-04-13 07:55:24 -06:00
src feat(editor): add view controls panel with auto-fit toggle and zoom slider 2026-04-14 23:21:11 -06:00
.envrc chore: migrate from flake.nix to devenv 2026-04-10 15:13:41 -06:00
.gitignore chore: update flake dependencies and add rustfmt config to gitignore 2026-04-14 22:46:05 -06:00
Cargo.lock chore(deps): add serde, ron, dirs, and rfd dependencies 2026-04-13 08:05:17 -06:00
Cargo.toml chore(deps): add serde, ron, dirs, and rfd dependencies 2026-04-13 08:05:17 -06:00
COPYING Add COPYING 2023-08-02 07:16:04 -05:00
devenv.lock chore: update flake lock dependencies 2026-04-14 23:15:42 -06:00
devenv.nix chore: migrate from flake.nix to devenv 2026-04-10 15:13:41 -06:00
devenv.yaml chore: migrate from flake.nix to devenv 2026-04-10 15:13:41 -06:00
flake.lock chore: update flake lock dependencies 2026-04-14 23:15:42 -06:00
flake.nix chore: migrate from flake.nix to devenv 2026-04-10 15:13:41 -06:00
LICENSE Add COPYING 2023-08-02 07:16:04 -05:00
README.md docs: update README for Bevy-based customizable character system 2026-04-13 13:28:39 -06:00

muni-tuber

A customizable vtubing app built with Bevy, made for municorn (me), but also for you!

Characters are defined as layered sprite hierarchies with spring physics, volume-reactive mouths, blinking eyes, and switchable expressions — all configurable through a built-in editor without touching code.

Features

  • Layered sprite characters — compose your character from any number of image layers arranged in a parent-child hierarchy
  • Volume-reactive mouths — up to four speaking phases (quiet, half-speak, speak, yell) driven by your microphone
  • Spring physics — child layers wobble and lag behind parent movement for a natural, bouncy feel
  • Blink animation — layers can blink on a random timer or on expression change
  • Breathing idle animation — subtle sinusoidal scale oscillation on the root layer
  • Expressions — named sets of per-layer image overrides switchable by hotkey
  • Built-in editor — live property editing, layer tree, threshold meters, and file management
  • Chroma key — configurable background color (yellow by default) for OBS or similar capture
  • RON character files — human-readable save format, stored in your platform data directory

Requirements

  • Rust (nightly toolchain; see devenv.nix or install via rustup)
  • Bevy 0.18 and its platform graphics dependencies (see Dependencies below)

Running

Clone the repository:

git clone https://codeberg.org/municorn/muni-tuber
cd muni-tuber

If you have devenv installed, all system dependencies and the Rust toolchain are provided automatically:

devenv shell cargo run

Without Nix

Install the system libraries listed under Linux dependencies and then run:

cargo run

Linux dependencies

Bevy requires several system libraries on Linux. Install the packages for your distribution, or use the devenv shell (see above) which provides them automatically via Nix.

Library Purpose
alsa-lib Audio capture (microphone input)
udev Device enumeration
vulkan-loader GPU rendering
libX11 X11 windowing
libXcursor X11 cursor support
libXi X11 input
libXrandr X11 display configuration
libxkbcommon Keyboard handling (Wayland)
wayland Wayland windowing
libGL / libGLU OpenGL (fallback renderer)

Creating a character

Character file format

Characters are saved as .ron files in your platform data directory:

Platform Path
Linux ~/.local/share/muni-tuber/characters/
Windows %APPDATA%\muni-tuber\characters\
macOS ~/Library/Application Support/muni-tuber/characters/

You can also open and save files from any location using the file dialog buttons in the editor.

Layer hierarchy

A character is built from a tree of layers. Each layer is a sprite image that can be attached to a parent at configurable anchor points.

Body (root layer)
├── Head
│   ├── Eyes      (can_blink = true)
│   ├── Hair strand 1
│   └── Hair strand 2
└── Accessory

Root layers have no parent. All other layers specify a parent_id matching their parent layer's id.

Pin points

Each layer has two anchor points, both expressed as normalized coordinates where (0.0, 0.0) is the top-left of the sprite and (1.0, 1.0) is the bottom-right:

Field Meaning
parent_anchor The point on the parent sprite where this layer attaches
self_anchor The point on this layer's sprite that aligns with the parent anchor

For example, to attach a head layer to the top-center of a body:

  • parent_anchor: (0.5, 0.0) — top-center of the body sprite
  • self_anchor: (0.5, 1.0) — bottom-center of the head sprite

Volume images

Each layer holds a set of images for each speaking phase:

Field Phase Fallback
quiet Silent (required)
half_speak Murmuring quiet
speak Talking half_speak
yell Very loud speak
blink Blinking current phase image

Only quiet is required. Any omitted variant falls back through the chain shown above.

Volume thresholds

The four speaking phases are separated by dBFS thresholds (negative numbers, where 0 is maximum loudness):

Threshold Default Meaning
half_speak 46 dBFS Minimum volume to enter HalfSpeak
speak 30 dBFS Minimum volume to enter FullSpeak
yell 5 dBFS Minimum volume to enter Yell

Adjust these in the editor's Volume thresholds panel while watching the live meter.

Spring physics

Layers with spring physics enabled lag behind parent motion, producing a natural wobble. Each layer has its own spring configuration:

Field Default Meaning
enabled true Whether spring physics run on this layer
stiffness 150 Restoring force (higher = snappier)
damping 12 Energy dissipation (higher = less bounce)
mass 1.0 Simulated mass (higher = more sluggish)
rotation_stiffness 150 Same as stiffness, applied to rotation
rotation_damping 12 Same as damping, applied to rotation

Set enabled = false for layers that should snap rigidly to their parent without delay (e.g. a rigid accessory glued to the head).

The underlying spring formula is a standard spring-damper:

force        = -stiffness × displacement  damping × velocity
acceleration = force / mass
velocity    += acceleration × dt
position    += velocity × dt

Rotation uses the same formula with rotation_stiffness and rotation_damping. Velocity is clamped each frame to prevent blow-up on large timesteps.

Expressions

An expression is a named set of per-layer image overrides. When an expression is active its overrides replace each referenced layer's default images. Layers not mentioned in an expression continue showing their defaults.

Expressions are activated by hotkeys or by using the preview button in the editor's expression panel.

Hotkeys

Hotkey bindings associate keyboard keys with actions:

Action Behaviour
SwitchExpression Toggle the named expression on/off (press again to deactivate)
HoldExpression Activate expression while key is held; deactivate on release
ForceBlink Immediately trigger a blink on all blinkable layers

Keys are specified by their Bevy KeyCode variant name, e.g. "F1", "KeyA", "Space". The editor's Hotkeys panel provides a key-capture mode — click Record, press the desired key, and the binding is set automatically.

Animations

Animation Description
Breathing Sinusoidal scale oscillation on the root layer (idle)
Pop Short scale impulse on the root layer when speaking starts
Blink Random blink (15 s interval, 0.2 s duration) per layer with can_blink = true

Blink is also triggered on every expression change.

Using the editor

Press Tab to toggle between editor mode and streaming mode.

Editor mode

The left panel contains all editing tools:

Section Purpose
Character picker Save, load, create, and name characters; set background color
Layer tree Hierarchical layer list; add, delete, duplicate, and reorder layers
Layer properties Edit the selected layer's name, parent, anchors, spring, and images
Volume thresholds Adjust dBFS thresholds with a live microphone meter
Expression editor Create, rename, and delete expressions; set per-layer image overrides
Hotkey configuration Add, remove, and record hotkey bindings

Streaming mode

Press Tab to hide the editor. The character fills the entire window, ready for stream capture. The background color (configurable in the character picker) defaults to yellow for chroma keying.

Keyboard shortcuts

Key Action
Tab Toggle editor panel on/off

All other shortcuts are user-defined hotkeys configured in the Hotkeys panel.

Dependencies

Crate Version Purpose
bevy 0.18 Game engine and ECS
bevy_egui 0.39 Editor UI overlay
cpal 0.16 Microphone input
serde 1 Serialization derives
ron 0.10 Character config format
dirs 6 Platform data directories
rfd 0.15 Native file dialogs
rand 0.9 Random blink intervals

Reporting issues

If you run into problems, don't hesitate to open an issue!