A 2D skeletal rigging, posing, and animation system
Project description
renpy-rigging
A 2D skeletal rigging, posing, and animation system for Ren'Py — including a full visual rig editor built in Ren'Py itself.
What is renpy-rigging?
- A lightweight 2D cutout/skeletal animation runtime for Ren'Py
- A visual rig & animation editor implemented as a Ren'Py project
- A pip-installable Python package that Ren'Py games can use to load and render rigs, poses, and animations at runtime
- A JSON-based, human-readable asset format for rigs, poses, animations, overlays, attachments, and scenes
The Guiding Principle
What you see in the editor is exactly what ships in your game.
No export pipelines. No mismatched math. No editor/runtime drift.
Features
Runtime Library (pip package)
- Load character rigs from JSON
- Load pose libraries and animation libraries
- Render characters using:
- Hierarchical joints (forward kinematics)
- Per-part pivots
- Per-part rotation, offset, scale
- Z-order layering
- Play animations:
- Pose sequences with timing
- Looping, one-shot, and ping-pong play modes
- Easing functions (linear, ease_in, ease_out, ease_in_out)
- Pose interpolation and blending
- Multi-track composite animations with weighted additive blending
- Variable playback speed
- Per-frame horizontal/vertical flipping
- Overlays: Layer clothing, accessories, and alternate art on top of (or replacing) existing parts — with slot-based auto-eviction for mutually exclusive items
- Attachments: Attach fully-rigged independent objects (weapons, props) to character joints — with their own skeletons, poses, animations, and slot-based swapping
- Scenes: Compose backgrounds, layered props, placed characters, and attachments into full environments with z-ordered layers and ambient sound
- Sound: Three layers of audio — per-pose sounds, animation timeline SFX (up to 4 concurrent channels), and scene ambient audio
- Outline/border rendering: Per-rig configurable borders and runtime-adjustable outline overlays with color, width, and alpha controls
- Fully Ren'Py-native:
- Uses Transform and displayables
- No engine hacks or C extensions
Editor (RigLab)
- Load a character rig and parts
- Visualize:
- Joints
- Bone hierarchy
- Part pivots
- Draw order
- Select and edit:
- Joints (drag with mouse, nudge with arrow keys)
- Parts (rotate, offset, adjust pivots)
- Save:
- Poses to poses.json
- Animations to animations.json
- Overlays to overlays.json
- Attachments to attachments.json
- Timeline editor:
- Build animations from poses
- Multi-track support with per-track weights and muting
- Set timing and easing per step
- Timeline sound placement
- Per-frame overlay and attachment overrides
- Preview loop, one-shot, or ping-pong playback
- Export:
- PNG frame export (transparent, solid-color, or scene background)
- Spritesheet export (all animations tiled)
- GIF export (pure-Python, no PIL required)
- Cloud render API for GIF/MP4
- Same renderer as the runtime library
Installation
Runtime Library
In your Ren'Py game's Python environment:
pip install renpy-rigging
Then in Ren'Py:
init python:
import renpy_rigging
Editor (RigLab)
- Open the
riglab/folder in the Ren'Py Launcher - Launch the project
- Select a sample character or add your own
Quick Start
Directory Layout
game/
characters/
vince/
rig.json # Joints, parts, canvas, border config
poses.json # Named poses (deltas from neutral)
animations.json # Named animations (frame sequences)
overlays.json # Named overlay sets (clothing, accessories)
attachments.json # Attachment bindings (weapons, props)
parts/ # PNG images for each body part
overlays/ # Overlay images per overlay set
sounds/ # Sound files
attachments/
tommy-gun/
rig.json # Attachment rig (joints, parts, attachment config)
poses.json
animations.json
parts/
scenes/
speakeasy/
scene.json # Background, layers, placed items/characters
parts/ # Scene prop images
Loading a Rig
init python:
from renpy_rigging import Rig, PoseLibrary, AnimationLibrary, RigRenderer
rig = Rig.load("characters/vince/rig.json")
poses = PoseLibrary.load("characters/vince/poses.json")
anims = AnimationLibrary.load("characters/vince/animations.json")
renderer = RigRenderer(rig, poses, anims)
Or load everything from a directory (including overlays and attachment bindings):
init python:
from renpy_rigging import RigRenderer
vince = RigRenderer.from_directory("characters/vince")
Showing a Character
# Show a static pose
show expression renderer.show_pose("neutral") as vince:
xalign 0.5
yalign 0.7
# Play an animation
show expression renderer.play_animation("idle", loop=True) as vince:
xalign 0.5
yalign 0.7
# Play an animation mirrored
show expression renderer.play_animation("walk", loop=True, flip_h=True) as vince
Using Overlays
# Add an overlay (slot-aware — evicts any overlay in the same slot)
renderer.add_overlay("broadway-brawlers-shirt")
# Remove an overlay
renderer.remove_overlay("broadway-brawlers-shirt")
# Query by slot
renderer.get_overlay_by_slot("shirt")
Using Attachments
# Attach a weapon (slot-aware — evicts any attachment in the same slot)
renderer.add_attachment("tommy-gun")
# Set the attachment's pose
renderer.set_attachment_pose("tommy-gun", "firing")
# Play the attachment's own animation
renderer.play_attachment_animation("tommy-gun", "fire", loop=True)
# Remove
renderer.remove_attachment("tommy-gun")
Setting Up Scenes
init python:
from renpy_rigging import SceneManager, register_channels
register_channels()
scene_mgr = SceneManager(os.path.join(renpy.config.gamedir, "scenes"))
scene_mgr.register_images() # enables "scene street" syntax
Then in your script:
# Using the scene manager
$ scene_mgr.show("speakeasy")
# Or with the custom rigscene statement (if registered)
rigscene speakeasy with dissolve
Scenes automatically composite backgrounds, layered props, placed characters, and attachments at the correct z-orders, and start ambient sound if configured.
Game Project Integration
You can integrate renpy-rigging into your Ren'Py game without pip installing the package. Add this to your options.rpy:
init -100 python:
import sys, os
src_path = os.path.normpath(os.path.join(renpy.config.gamedir, "..", "..", "src"))
sys.path.insert(0, src_path)
Then import and re-export into the Ren'Py store in a rig_runtime.rpy file:
init -10 python:
from renpy_rigging import (
Rig, Pose, PoseLibrary, AnimationLibrary,
RigRenderer, SceneManager, register_channels,
)
register_channels()
This is the pattern used by both the RigLab editor and the RigLabDemo game.
Data Formats
rig.json
Defines the skeleton (joints) and visual parts:
{
"canvas": [600, 900],
"joints": {
"root": {"parent": null, "pos": [300, 700]},
"pelvis": {"parent": "root", "pos": [0, 0]},
"chest": {"parent": "pelvis", "pos": [0, -200]}
},
"parts": {
"torso": {
"image": "parts/torso.png",
"parent_joint": "pelvis",
"pos": [0, 0],
"pivot": [130, 135],
"z": 10
}
},
"border": {"enabled": true, "width": 3, "color": "#000000"},
"slots": ["weapon"]
}
poses.json
Poses store only deltas from the neutral rig:
{
"neutral": {
"joints": {},
"parts": {}
},
"wave": {
"joints": {
"shoulder_R": {"rot": -120},
"elbow_R": {"rot": 90}
},
"parts": {
"hand_R": {"visible": false}
},
"overlays": {"hand-pistol": true},
"attachments": {
"tommy-gun": {"pose": "firing", "visible": true}
},
"sound": "sounds/whoosh.wav"
}
}
animations.json
Animations are sequences of poses with timing. Three formats are supported (all backward-compatible):
Simple (flat list):
{
"idle": [
{"pose": "neutral", "ms": 500},
{"pose": "breathe_in", "ms": 800, "ease": "ease_in_out"},
{"pose": "neutral", "ms": 500},
{"pose": "breathe_out", "ms": 800, "ease": "ease_in_out"}
]
}
With timeline sounds:
{
"fire": {
"frames": [
{"pose": "firing", "ms": 80},
{"pose": "neutral", "ms": 80}
],
"sounds": [
{"path": "sounds/gunshot.wav", "start_ms": 0},
{"path": "sounds/shell_casing.wav", "start_ms": 100}
]
}
}
Multi-track composite:
{
"walk_and_breathe": {
"tracks": [
{"name": "Walk", "frames": [...], "weight": 1.0},
{"name": "Breathe", "frames": [...], "weight": 0.5, "muted": false}
],
"sounds": [...]
}
}
overlays.json
Overlays associate extra art with body parts:
{
"broadway-brawlers-shirt": {
"_slot": "shirt",
"torso": {"path": "torso.png", "replace": true},
"arm_upper_L": true,
"arm_upper_R": {"z_delta": 2}
}
}
A part set to true uses all defaults (same part name as the image filename, layered on top). The _slot field enables auto-eviction when activating a new overlay in the same slot.
attachments.json (on the character)
Defines which attachments a character can use and how they connect:
{
"tommy-gun": {
"path": "attachments/tommy-gun",
"default_joint": "wrist_R",
"pos": [-5.0, 29.0],
"rot": 90.0,
"slot": "weapon"
}
}
Attachment rig.json
An attachment's own rig.json includes an attachment config block:
{
"name": "tommy-gun",
"canvas": [1051, 345],
"joints": {"root": {"parent": null, "pos": [525, 172]}},
"parts": {...},
"attachment": {
"is_attachment": true,
"default_attach_joint": "wrist_R",
"inherit_rotation": true,
"inherit_scale": false,
"z_offset": 10,
"slot": "weapon"
}
}
scene.json
Defines a composed environment with backgrounds, layers, and placed entities:
{
"canvas": [1456, 816],
"background": {
"color": "#000000",
"image": "parts/speakeasy.png",
"image_offset": [0, 0],
"image_scale": 1.0
},
"layers": {
"background": {"z": -100},
"foreground": {"z": 100}
},
"items": [
{
"name": "barrel",
"image": "parts/barrel.png",
"layer": "background",
"pos": [300, 400],
"scale": [1.5, 1.5],
"z_offset": 5
}
],
"characters": [
{
"name": "Vince",
"source": "Vince-New",
"layer": "background",
"pos": [728, 408],
"scale": [0.4, 0.4]
}
],
"attachments": [
{
"name": "Tommy-Gun",
"source": "Tommy-Gun",
"layer": "background",
"pos": [188, 578],
"scale": [0.6, 0.6]
}
],
"sound": "sounds/jazz.ogg",
"sound_loop": true
}
Repository Structure
renpy-rigging/
README.md
pyproject.toml
LICENSE
src/
renpy_rigging/
__init__.py
rig.py # Rig data structures (joints, parts, canvas)
pose.py # Pose application, blending, interpolation
animation.py # Animation playback (multi-track, sounds, easing)
overlay.py # Overlay system (clothing, accessories)
attachment.py # Attachment system (weapons, props)
scene.py # Scene composition (backgrounds, layers, entities)
render.py # Ren'Py displayable builders
audio.py # Audio channel registration and management
math2d.py # Vec2, Transform2D, easing functions
io.py # JSON load/save helpers
riglab/ # The Ren'Py editor app
game/
script.rpy
options.rpy
gui.rpy
screens.rpy
riglab/
core/
rig_runtime.rpy
rig_io.rpy
rig_math.rpy
editor/
editor_screen.rpy
editor_input.rpy
timeline_screen.rpy
data/
samples/
main_menu.rpy
riglabdemo/ # Demo game project
game/
characters/ # Character rigs
attachments/ # Attachment rigs
scenes/ # Scene definitions
docs/
rig_format.md
authoring_guide.md
renpy_integration.md
Editor Keyboard Shortcuts
| Key | Action |
|---|---|
| J | Toggle joint visibility |
| B | Toggle bone visibility |
| P | Toggle pivot visibility |
| Arrow keys | Nudge selected joint (1px) |
| Shift + Arrow keys | Nudge selected joint (10px) |
| Space | Play/pause animation (in timeline) |
Design Philosophy
- FK-first: Forward kinematics only in v1
- No sprite sheets: Everything is runtime-composed
- Human-editable JSON: All data formats are readable and editable
- Editor and runtime share the same renderer
- Procedural-friendly: Built for blending, aiming, reacting, not just canned loops
- Slot-based resource management: Overlays and attachments use named slots for clean swapping (e.g., only one "weapon" or "shirt" at a time)
Development Roadmap
- v0.1 — Viewer: Load rig.json, render neutral pose
- v0.2 — Poses: Load poses.json, apply pose deltas
- v0.3 — Editor: Select + drag joints, rotate parts, save poses
- v0.4 — Animation: Timeline editor, playback system, export
- v0.5 — Overlays, attachments, scene rendering, composite animations with sound
Future
- IK helpers
- Symmetry tools
- Onion skinning
- Event markers in animations
- Physics jiggle bones
- Auto-z reordering helpers
Non-Goals
This is not a Spine replacement. This is not a general-purpose animation suite.
This is a Ren'Py-first, game-dev-first rigging system.
Who This Is For
- Visual novel devs who want live characters
- 2D game devs who want:
- Aiming arms
- Breathing idles
- Reactive poses
- Procedural animation
- Artists who think in volumes and joints, not sprite sheets
Documentation
License
MIT License - see LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file renpy_rigging-0.4.0.tar.gz.
File metadata
- Download URL: renpy_rigging-0.4.0.tar.gz
- Upload date:
- Size: 58.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b78c2de1a23af68477e3c9e585c66d6e9f3ff20147367f75d612bd6fccb64c9
|
|
| MD5 |
5c393acb3797f44668d74d4d1f343633
|
|
| BLAKE2b-256 |
a1a4062a1ee3d3e45a0df04843125a86b3d1b264d04e19391fbecd45f682687b
|
File details
Details for the file renpy_rigging-0.4.0-py3-none-any.whl.
File metadata
- Download URL: renpy_rigging-0.4.0-py3-none-any.whl
- Upload date:
- Size: 63.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fecfa81256aa17427948e7e6eb1a3212c465919e37307e0bf46e7932cb6eb836
|
|
| MD5 |
0bdbfe40b8c2fa80d53c765d3971ce15
|
|
| BLAKE2b-256 |
d21ed757f3f4f7b254e28e6631fcd68f5615c2ff9b518fef65a67df6a2c98f0e
|