| name | roblox-animation |
| description | Triggers on /roblox-animation only. Two subcommands - `author <name>` generates a placeholder KeyframeSequence Luau module; `import <url-or-path>` converts a Roblox Marketplace URL or a Mixamo BVH file into the same Luau module shape. Output is editable in Studio's Animation Editor. |
| argument-hint | [author <name>] | [import <url-or-path>] |
/roblox-animation
Author or import Roblox animations as KeyframeSequence Luau modules the dev can visually tune in Studio.
Triggers
/roblox-animation followed by a subcommand:
/roblox-animation author kick - hand-authored placeholder from a name
/roblox-animation import https://create.roblox.com/store/asset/2515090838/Kick - Marketplace URL passthrough
/roblox-animation import assets/animation-imports/kick.bvh - parse a BVH file
Never auto-invoke. The dev picks the subcommand explicitly.
Why this exists
Authored animations are clunky: the dev wants source-controlled, diffable, iteratively-improvable animations. Two import paths cover the realistic library:
- Roblox Marketplace already has thousands of free animations - just wire the asset ID
- Mixamo (Adobe) has industry-grade humanoid animations exportable as BVH
The skill ships consistent KeyframeSequence Luau output across both paths so the dev can visually tune any of them in Studio's Animation Editor.
Subcommand: author
Generates a placeholder KeyframeSequence module from scratch.
-
Detect rig. Read game-packages/ball-sim/src/Config.luau (or equivalent) for playerHeight and playerRadius. Default to R15 unless the dev's project clearly uses R6.
-
Decide save location. Look for existing animation files. Common locations: src/ReplicatedStorage/Animations/, game-packages/animations/src/, src/Shared/Animations/. If none, create src/ReplicatedStorage/Animations/ - it auto-mounts via the project's existing src/ReplicatedStorage Rojo entry.
-
Author the keyframes. Build a 5-8 keyframe motion as a storyboard comment at the top of the file, then emit a Luau module with the canonical structure (see template under ## Output format below). Include named marker events at the contact / climax moment so game code can listen via track:GetMarkerReachedSignal(name).
-
Emit a smoke test. REQUIRED. Place at tests/animations/<Name>.spec.luau. Must assert: module shape (NAME / PRIORITY / LOOPED / build), build() returns a KeyframeSequence (catches Plugin-capability + invalid-enum errors at jest time, not playtest time), at least 2 keyframes emitted, each Keyframe has a HumanoidRootPart root Pose, Time values monotonically increasing. Reference: tests/animations/Kick.spec.luau. Without this test, the dev only hits errors at Studio playtest - shipped 2026-05-17 after exactly that bit twice (AuthoredHipHeight Plugin-gate, Enum.PoseEasingStyle.Quad doesn't exist).
-
Verify build + tests. Run bash scripts/check.sh (or rojo build if no check script). Must report green before commit.
-
Report. Path, bones touched, marker events with timestamps, integration snippet, visual-tune instructions.
Subcommand: import
Two input forms:
Form A - Roblox Marketplace URL
URL pattern: https://create.roblox.com/store/asset/<ID>/<slug> or https://www.roblox.com/library/<ID>/<slug>.
- Extract the asset ID from the URL (the numeric segment after
/asset/ or /library/).
- Verify the asset exists by fetching the Marketplace page. Confirm it is an Animation (not a model or audio). If unclear, ask the dev.
- Emit a Luau stub under the save-location convention from the author flow:
local Animation = {}
Animation.NAME = "<slug>"
Animation.ASSET_ID = "rbxassetid://<ID>"
Animation.PRIORITY = Enum.AnimationPriority.Action
Animation.LOOPED = false
function Animation.create(): Animation
local anim = Instance.new("Animation")
anim.AnimationId = Animation.ASSET_ID
return anim
end
return Animation
- Report. Asset ID, source URL, save path, integration snippet (
local Anim = require(...); local track = humanoid.Animator:LoadAnimation(Anim.create()); track:Play()).
Marketplace assets are NOT visually editable - they are published bytes. If the dev wants to tune, suggest exporting via the Animation Editor's "Open Existing Animation" + their rbxassetid, modifying, and re-publishing as a new asset.
Form B - BVH file path
BVH is plain text. The dev grabs it from Mixamo (Export -> Format: "Without Skin" -> BVH) or any BVH library and drops it in a known folder (default: assets/animation-imports/<name>.bvh).
- Confirm Python is available via
python --version or python3 --version. If neither, tell the dev to install Python 3.10+ and stop.
- Run the helper script
bvh_to_keyframes.py (sidecar in this skill folder):
python ~/.claude/skills/roblox-animation/bvh_to_keyframes.py <input.bvh> <output.luau> [--name <NAME>] [--priority Action|Movement|Idle|Core|Action2|Action3|Action4]
The script parses joint hierarchy + per-frame rotations, remaps Mixamo / standard BVH joint names to Roblox R15 bone names, samples to ~30 keyframes evenly (or fewer if the animation is short), and emits the same KeyframeSequence Luau shape as the author subcommand.
- Verify build.
rojo build default.project.json -o build/<placefile>.
- Report. Frame count, keyframe count, duration, bones touched, save path, integration snippet.
If a BVH joint has no Roblox equivalent (Mixamo has fingers, Roblox R15 stops at hands), the script logs a warning and drops it.
Output format
All paths emit the same Luau shape: a module exporting Animation.build(): KeyframeSequence (author + BVH import) or Animation.create(): Animation (Marketplace import). This consistency lets game code consume both paths identically.
Template for the build-side (author + BVH):
local Animation = {}
Animation.NAME = "<Name>"
Animation.PRIORITY = Enum.AnimationPriority.Action
Animation.LOOPED = false
local function pose(parent, boneName, cf, easing, dir) ... end
local function keyframe(seq, time, name?) ... end
local function buildPoseTree(kf, poses, easing, dir) ... end
function Animation.build(): KeyframeSequence
local seq = Instance.new("KeyframeSequence")
seq.Name = Animation.NAME
seq.AuthoredHipHeight = 2
seq.Priority = Animation.PRIORITY
seq.Loop = Animation.LOOPED
return seq
end
return Animation
Existing reference: src/ReplicatedStorage/Animations/Kick.luau in socka_heads.
Visual editing round-trip in Studio
For build-side modules (author + BVH import): instantiate via local seq = require(...).build() in Studio's command bar, parent to workspace, right-click in Explorer, "Edit in Animation Editor", drag bones / retime / tweak easing, then "..." menu -> Export -> Publish to Roblox. Drop the returned rbxassetid://N into your Animation instance.
For Marketplace imports: the asset is already published. To tune, the Animation Editor's "Open Existing Animation" accepts an rbxassetid and re-published versions get new IDs.
KeyframeSequenceProvider:RegisterKeyframeSequence is dev-mode-only. Shipped games must use a real published asset ID.
Plugin-capability gated properties (do NOT write from runtime scripts):
KeyframeSequence.AuthoredHipHeight, KeyframeSequence.Priority, and KeyframeSequence.Loop can only be written from a Plugin context. Runtime scripts get "lacking capability Plugin" errors. In emitted modules, skip these writes in build() and have the caller set the equivalents on the AnimationTrack after loading:
local track = animator:LoadAnimation(anim)
track.Priority = Animation.PRIORITY
track.Looped = Animation.LOOPED
AuthoredHipHeight has no runtime equivalent — Roblox uses the rig's default hip height. Don't emit a write for it.
Valid Enum.PoseEasingStyle values are limited: Linear, Constant, Elastic, Cubic, Bounce. Do NOT use Quad, Sine, Back, Exponential etc. — those are Enum.EasingStyle (for tweens), not Enum.PoseEasingStyle. Skill output should use Cubic as the default smooth ease, Linear for sharp transitions.
Quality limits
The author flow ships placeholders. The import flow ships whatever the source had - Mixamo BVH animations look great after re-targeting; Marketplace varies by author. The skill is best at: discrete short motions, single-character animations. Worst at: paired (two-character) interactions, weight-shifted motion that depends on inverse kinematics.
Out of scope
- FBX parsing (v2). Use Mixamo's BVH export option for now.
- Video / YouTube / TikTok URL pose extraction. Possible in theory, low quality in practice.
- Auto-publish to Roblox marketplace. Requires manual Studio publish step.
- Animation blending state machines. The dev wires the resulting track into their state code.