CoolFace
Datasetpublic

sprited/dancing-chibi-figures

Dancing Chibi Figures — v0.1 One template Q-version (chibi) character, animated by 1,430 motion clips, rendered with exact labels and motion-grounded captions — and paired frame-for-frame with Dancing Stick Figures. 1,423 clips · 6 s @ 20 fps · 128×128 RGBA · 514,800 frames · 143 text prompts × 10 seeds × 3 cameras · every frame carries the 3D skeleton, camera, depth, camera-space normals, part segmentation, motion events and five levels of caption. One row per motion group;… See the full description on the dataset page: https://huggingface.co/datasets/sprited/dancing-chibi-figures.

sourceHugging Facecc0-1.0updated 1mo agoView on Hugging Face
1likes482downloads
Dataset Card

Dancing Chibi Figures — v0.1

One template Q-version (chibi) character, animated by 1,430 motion clips, rendered with exact labels and motion-grounded captions — and paired frame-for-frame with [Dancing Stick Figures](https://huggingface.co/datasets/sprited/dancing-stick-figures).

1,423 clips · 6 s @ 20 fps · 128×128 RGBA · 514,800 frames · 143 text prompts × 10 seeds × 3 cameras · every frame carries the 3D skeleton, camera, depth, camera-space normals, part segmentation, motion events and five levels of caption.

<p align="center"><img src="figs/contact_sheet.png" width="900"></p>

One row per motion group; every clip also carries depth, camera-space normals, part segmentation and joints:

<p align="center"><img src="figs/labels_row.png" width="1000"></p>

The stick-figure dataset was the minimal case (lines, an exact oracle). This is the next rung: a real character with volume — a grey, flat-shaded chibi mannequin (~2.8 heads tall, face guide cross, dark outline, the kind of template figure drawing books use) — while every pixel is still a deterministic function of ~30 known parameters. Same motion, same cameras, same clip_id as the stick-figure dataset, so the two can be used together (skeleton → character, stick render ↔ chibi render, pose estimation on chibi proportions).

v0.1 = first public cut. Rendering, labels and splits are final for this version. Captions are templated from the motion labels (see Captions) and may be refined in v0.2 without re-rendering.

Which config?

configrowssizecontents
frames (default)514,800 frames5.1 GB128² RGBA colour (WebP lossless) + depth16 + normals + seg + all labels + captions
mini514,800 frames2.09 GB64² version of frames — laptops, Colab, classrooms
motionthe raw ARDY motion is identical to the stick dataset's motion config (same clip_id); the chibi retarget is generator/chibi.py in the code repo

Quick start

python
from datasets import load_dataset
ds = load_dataset("sprited/dancing-chibi-figures", "frames", split="validation")
row = ds[0]
row["color"]              # PIL RGBA image, transparent background
row["caption"]            # "Seen from the front-right, a chibi mannequin waves hello with the right hand; it raises the right hand to head level at 0.5 s ..."
row["caption_dynamic"]    # observed motion only (from the labels, never from the prompt)
row["prompt"]             # the text the motion was generated from: "A person waves hello with the right hand."
import json, numpy as np
events = json.loads(row["events"])                                    # per-clip motion events (same on every row of a clip)
xy = np.frombuffer(row["joint_xy"], np.float32).reshape(27, 2)       # normalised [0,1] image coords
seg = np.array(row["seg"])                                            # bone id per pixel (0 = background)

Composite the RGBA colour over any background you like; depth/normal/seg let you relight or recolour it (the render is unlit on purpose — the normals are there so you can "afterlight" it).

What is in a frame

`frames` / `mini` — one row per rendered frame.

columntypemeaning
sample_id, clip_id, frame_idx, n_frames, fpsstr/intclip_id = group/prompt_slug_s{seed}/c{cam}; 120 frames per clip, 20 fps; identical ids to Dancing Stick Figures
split, group, held_outstr/boolsplit ∈ train/val/test; group ∈ dance, gesture, locomotion, transitions, idle, acrobatic, sport (held out → test only)
prompt (= text)strthe ARDY motion prompt the clip was generated from (143 unique) — the intent, not a description of the clip
eventsJSON str (identical on every row of a clip-camera)rule-based motion events from the labels: travel_m, travel_dir, heading_change_deg, jumps [[t,dur]], steps, left/right_hand_above_head / _raised / _forward, left/right_foot_raised, low_posture, lying, inverted, hips_height_m, activity_per_s, active_s
caption_staticstrappearance + framing + camera (constant over the clip)
caption_dynamicstrthe observed motion in temporal order, generated from events only
caption_short, caption, caption_densestr5–10 words / 1–2 sentences / 3–6 sentences; 2–3 paraphrase templates sampled per clip-camera
seedintARDY seed 0–9
qa_flagsstrcomma list; prompt_mismatch:no_locomotion / no_jump / no_turn / no_low_posture / no_arms_up (heuristics: the clip contradicts its prompt), frozen, out_of_frame (a joint centre leaves the image — 0.31 % of frames, almost all in transitions clips that start on the floor and stand up; the fixed camera is framed on the frame-0 hips). Kept, not filtered
cam_yaw, cam_pitchfloat (rad)orthographic camera, same sampling and values as the stick dataset; yaw 0 = figure faces the camera; positive pitch = camera slightly below the horizontal
cam_center_x/y, px_per_mfloatprojection in 128-px units: x_px = cx + px_per_m·x (for mini divide by 2); scale 50–58 px/m
joint_xyzbinary f32[27,3]figure-frame 3D joints (x left, y up, z forward), metres, Hips at frame 0 = origin
joint_xybinary f32[27,2]image coordinates, normalised to [0,1]
joint_depthbinary f32[27]depth toward the camera (m), Hips-relative, same convention as the depth map
joint_visiblebinary u8[27]1 if the bone owns ≥1 pixel in `seg` — NOT the stick dataset's per-joint occlusion test, despite the same column name; hand-tip and toe bones are absorbed by their neighbours in seg and are always 0 (use joint_xy for an in-frame test). Will be unified with the stick semantics in v0.2
root_pos, root_vel, root_headingbinary f32[3], f32[3], f32[2]Hips trajectory in the frame-0 figure frame (world travel — the render itself is hips-centred); heading = (cos, sin) yaw
foot_contactsbinary u8[4]ARDY foot contacts: LeftFoot, LeftToe, RightFoot, RightToe
colorimage128×128 RGBA WebP (lossless), transparent background, unlit flat grey
depthimage16-bit PNG, depth = lo + u16/65535·(hi−lo), range [−1.5, 1.5] m toward the camera around the Hips, 0 = background
normalimageRGB WebP (lossless), camera-space normal n = rgb/255·2−1 (x right, y up, z toward camera)
segimage8-bit PNG, value = joint id + 1 of the bone owning the pixel (0 = background); exact at render resolution, majority-vote downsampled
config, sizestr/intframes/128, mini/64

All left/right in prompts, captions, events and joint names are the figure's own left/right (egocentric), not the viewer's — a figure facing the camera points "to the left" toward screen-right.

Binary columns are raw little-endian arrays: np.frombuffer(row[col], dtype).reshape(shape). Skeleton (cskel27, index order): Hips, Spine, Spine1, Spine2, Spine3, Neck, Head, RightShoulder, RightArm, RightForeArm, RightHand, RightHandEnd, RightHandThumb1, LeftShoulder, LeftArm, LeftForeArm, LeftHand, LeftHandEnd, LeftHandThumb1, RightUpLeg, RightLeg, RightFoot, RightToeBase, LeftUpLeg, LeftLeg, LeftFoot, LeftToeBase. Head is the head-sphere centre (skull base in the stick dataset).

Captions: prompt ≠ caption

The ARDY prompt is what we asked for; the clip is what the motion model produced, and they disagree more often than you would think ("walks in a circle" turns 62°; "does jumping jacks" jumps twice, then stands). So the captions are generated from the labels, Seedance-style: a static part (appearance, framing, camera), a dynamic part (events in temporal order), and three granularities, with the prompt kept as a separate prompt column and a prompt_mismatch flag when the two clearly contradict. The render is hips-centred, so the captions say "the camera follows the hips" and never claim visible travel; travel lives in events / root_pos.

We also ran an off-the-shelf video VLM (Qwen3-VL-8B) on the clips: it described ~half of them correctly (sitting, waving, kicking) and missed whole-body fast motion (jumps, a flip) and turning on this featureless mannequin — a useful probe, not a caption source; VLM captions are not included in v0.1.

Splits

By prompt, never by seed or camera (identical to the stick dataset): sport is held out entirely; else hash(prompt) → 90/5/5.

frames
train363,600
validation18,000
test (incl. sport)133,200

How it was made

prompt → NVIDIA ARDY text-to-motion (6 s, 20 fps, seeds 0–9; the same clips as the stick dataset) → chibi retarget (same joint rotations on chibi bone lengths; root travel scaled by the leg ratio so the feet do not slide; ground from foot contacts) → Blender 5.1 procedural rig (Skin-modifier mannequin over the 27-joint graph + sphere head, heat weights, dual-quaternion skinning; flat Emission material, inverted-hull outline, face guide cross) → 3 orthographic cameras per clip (same sampling as the stick dataset) → two headless Eevee passes per frame (anti-aliased colour; 1-sample exact depth / normal / segmentation) at 256², box-downsampled to 128 / 64 → parquet. Deterministic from clip_id; the generator is in the code repo.

Intended use / limitations

Teaching and benchmarking small video/image generative models, conditional generation (skeleton/seg/depth → character), pose estimation on chibi proportions, sprite/character animation research. One template character only (no clothes, hair, faces, props); hips-centred framing (no visible travel); ~0.3 % of frames are partly out of frame (flagged out_of_frame); the rig mesh has small internal defects at the neck and wrists invisible at ≤256 px; prompt_mismatch flags are heuristics; camera pitch sign follows the stick dataset (positive = from slightly below).

Baselines & tutorial

Two tracks, both trained on mini (64px):

🤗 diffusers-standard — load with three lines, everything transfers to any diffusers project:

python
from diffusers import DiffusionPipeline
pipe = DiffusionPipeline.from_pretrained("sprited/dancing-chibi-figures-t2i-64",
                                         custom_pipeline="sprited/dancing-chibi-figures-t2i-64",
                                         trust_remote_code=True)
imgs = pipe(["a person jumps in place"] * 4, num_inference_steps=50, guidance_scale=3.0).images

pure PyTorchsprited/dancing-chibi-figures-baselines: a 64px image model and a text-conditioned autoregressive video model (chunked diffusion, CLIP prompt embeddings); trainer and scripts/rollout.py in the code repo. A ComfyUI node (comfy/qversion-chibi) wraps the image checkpoints.

📓 Colab tutorialdancing_chibi_figures_colab.ipynb: look at the data, train the text-to-image model from scratch, turn it into a video model, grade it with a counting robot — free-T4-sized, written for absolute beginners. (Sequel to the stick-figures notebook.)

Versioning

v0.1 (2026-08) first public cut. Planned v0.2: more prompts (a curated set of ~300 from a chibi pose reference book), an anatomy oracle, post-processed outlines, optional colour-coded config, 8-direction sprite views.

License and attribution

Data CC0-1.0. Motion generated with NVIDIA ARDY (NVIDIA Open Model License, which claims no ownership of outputs); character, rig and renderer are procedural code (MIT) — no third-party 3D assets. Paired dataset: sprited/dancing-stick-figures.