Maya animation (animator)
Expert animation means every frame is a choice: poses that read as drawings from the camera, holds
and snaps placed on purpose, designed spacing, weight shown by resistance and a caught stop. Work
big to small (story, body, face, detail); never add detail to a stage that does not read yet. An
agent cannot scrub, so it measures (world and screen tracks, spacing, holds, contacts, a ballistic
fit, overlap lags), then looks at a playblast or a headless sheet before calling anything done. If a
sibling skill named here is missing from your available skills, ask the user to install it
(npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and
flag the gap.
REQUIRED BACKGROUND: scenario-maya-expert (execution channel, review loop, 2027 version traps).
Toolkit: scripts/mx_anim.py. Its analysis and image layer is pure Python and
ran offline.
Stance (the expert delta)
- Pick the workflow per shot; blocking ends when the information is in. Neistadt: pose to pose
for acting, straight ahead or extremes for flow; blocking is done when all five key categories
exist (main, anticipation, overshoot and follow-through, breakdown, hold). Elver splines physical
shots from the start; Camporota proves timing on two boxes first.
- Maya is "the worst in-betweener in the world" (Newman). Feed it keys: ones on fast actions, 2
to 4 frames apart on slow parts. Spline readiness is a test: spline the range, play; mush means
back to stepped with more poses. Native Set Key reshapes neighboring curves (Wade): insert keys,
prove it with a curve diff, and share keys across every control before splining.
- Weight is resistance, then release, then a caught stop. Wade: show what the character is up
against (a delay, acceleration from rest) before its strength; strength is a frame count. Elver:
more up and down reads as a balloon, not weight; weight is a fall caught abruptly, a bigger hold
in the squash, residual jiggle. Heavy air is gravity-true [added physics]; Wade's weighted apex
adds hang, for light or cartoony bodies.
- Feet and spine sell every weight shift (jump, hop, landing, lift). Camporota: animate the foot
roll in every crouch, it sells the compression; in a big drop the spine drags behind the hips and
only the arms drop; first passes overdo the anticipation, so dial it back; a touch-down goes flat
"almost in one frame". Elver: on the first airborne frame the toe tip points back to where it left
the ground.
- Judge in camera space, at speed. Elver: a motion trail can show a perfect 3D arc that fails on
screen. Lazare: the eye goes to the biggest motion, so the non-focus character stays
"appropriately still". Project tracks through the shot camera; read the playblast before any
metric.
- The base layer is the animation; mocap is precious. Newman: a layer is a time-boxed experiment
with marker keys and a protective key; never clean curves on it, merge with Smart Bake, then clean
(Elver: only a late global note or an additive tremble, then bake). On mocap fix only the defect
asked for, or the cascade ends "not hand key and not mocap"; after a merge, Euler filter, then
delete keys down to extremes.
- Lip sync animates sounds, not words. Santos: jaw only first (the muppet pass), then the big
five (jaw, two corners, top and bottom lip) for 90%; lead by place of articulation, bilabials on
the frame. Lazare: shift every mouth key 1 to 2 frames earlier only when the whole line plays
late.
- Polish is triage; review is top down. Elver ranks the notes, spends on the ones that pay, owns
every contact frame. Newman grades Intent, Mechanics, Performance, Integration; a playblast does
not prove game feel.
Establish first
| Input | Why it changes the plan | Default when the brief is silent |
|---|
| Style and destination | squash, stops, closeness to reference: cartoony pushes; live action needs pose squash and no dead stops (Elver); VFX stays near reference (Neistadt) | realistic feature quality |
| Frame rate | timing numbers, currentUnit | 24 fps (film) |
| Weight and strength | frames of resistance and of overcome (Wade: decide both numbers first) | heavy: resistance beat of 2+ frames, no hang |
| Rig | control names, IK/FK, spaces, foot roll attribute (map once with listAttr(keyable=True)) | hands in IK, no IK/FK or space switches inside a shot (Newman) |
| Camera | a locked shot camera is the only judge; games also need every angle and the engine | build the shot camera, lock it |
| Reference, audio | landmarks (contacts, takeoff, apex, landing, blinks, darts) give rhythm, then push (Neistadt) | beat list from the brief |
| Game metrics | speeds and heights agreed in round numbers before animating (Newman); root motion or in place (Epic) | in place, metrics sheet requested |
Workflow
- Plan in numbers. Beats with frames, the leading part per beat, key categories, contacts,
windows (crouch, push, air, impacts), style;
A.store_plan(plan). Physical shots:
A.air_frames(rise, drop) budgets the air. Dialogue: phrases with subtext and an action verb,
ruled sounds (M, B, P, F, V, L, T, D), 2 to 4 tentpoles (Santos, Wade). GATE: frame counts add
up.
- Set up. fps, range, sound, shot camera locked; query the tangent defaults (2027 notes). GATE:
mx_validate.validate(profile="shot", rules={"fps": 24}) has no fail.
- Proxy pass (big, fast or camera-bound moves).
A.proxy_boxes(), key translate and lean, air
with A.ballistic_keys(), rotation 1 to 2 frames behind (Camporota). The first anticipation is
usually too big: A.scale_curve(root, "translateY", 0.8, rest_y, time=crouch). GATE:
A.evaluate_gates() on the proxy (ballistic, catch, resistance, push_fastest);
proxy_curves.png looked at.
- Blocking, stepped.
A.set_key_defaults("blocking"); A.key_pose(pose, f, fill=ctrls) keys
every control on every pose; passes: main, anticipations, overshoots, breakdowns (favor a
neighbor, never 50/50), holds (A.key_hold()); A.share_keys(ctrls). Never act from the neutral
rig pose (Neistadt). Crouch poses: foot roll keyed, spine dragging, arms hanging (Camporota).
GATE: every key category present; stepped playblast (GUI) or tracks.png; write the story it
reads, then check silhouettes.
- Spline pass 1, the core.
snap = A.snapshot(ctrls) (the buffer curve); A.to_spline() on
COG, spine, legs only; face, eyes, fingers stay stepped (Camporota, Santos). Air gravity-true;
broken tangent into a hard landing (Newman); planted feet flat. Mush: A.restore(snap), add
poses. Every later key: A.insert_key(), then A.curve_diff() on sampled snapshots is ok
outside its two segments (P4). GATE: A.run_gates(plan) ballistic, hang, catch, slide,
penetration, foot_roll, spine_drag, heel_to_flat pass; no A.overshoots() through holds.
- Spline pass 2, overlap. Arms, head, secondary: correlated chains edited together first
(Newman), then
A.offset_keys() by 1 to 2 frames down the chain. GATE: overlap lags not all
zero; pose_to_pose_tell low; tracks clean in camera.
- Face, if the shot has one. Body first, then the mask (eyes, brows), then the mouth: muppet
jaw keyed at
A.mouth_keys(phonemes), big five, tongue, connection by A.copy_curve() (nostrils
1 frame behind the corners, dampened). Eyes linear, 2-frame darts; blinks close faster than they
open. GATE: A.jaw_report(), A.lip_lag() against A.audio_envelope(), A.blink_report(),
A.transitions(); close-ups on tentpoles.
- Polish, by triage. Fix the top-ranked notes, one section at a time. Contacts and fast moves
on ones; peel-off on every takeoff; impacts get a pushed squash and a bigger hold; pops become 3
frames on an arc plus an overshoot;
A.retime() tightens a section; moving holds; a
motion-blurred render before final (all Elver). GATE: every gate passes (peel_off, impact_hold
included), layers merged, no sub-frames, Euler filtered, critique.md on the final playblast.
Variants. Cycle: poses every 3 frames, loop with A.cycle() before polish, offsets inside the
cycle keep the period. Game clip: metrics, A.cycle_speed(), loop seam, root rules, then
scenario-maya-pipeline-scripting. Mocap: procedures P12; retarget: P13; heavy interactions: P9.
Numbers
| Value | Relative to | Source |
|---|
| Gravity 1.703 cm/frame^2 (981/576) | 24 fps, scene in cm | physics [added] |
| Walk keys every 3 frames: contact 1, down 4, pass 7, up 10, contact 13; loop 1 to 25 | 24-frame cycle | Camporota |
| Touch-down to flat foot about 1 frame, at most 2; walk arm offsets 0, 1, 2, head 1 | every step and landing | Camporota; body digest |
| Rotation 1 to 2 frames behind translation | proxy and body | Camporota |
| Keys on ones on fast actions; 2 to 4 frames apart on slow ones | blocking | Newman |
| Blink close 2 / open 4 (or 3 / 5; big eyes one more) | frames | Elver |
| Eye dart 2 frames, linear; brows lead lids by 1 to 2, never 5 | frames | Elver |
| Snappy move: 3 frames on an arc plus a small overshoot, never 1 | frames | Elver |
| Global lip shift 1 to 2 frames earlier; opens near full on frame 1 | when late at speed | Lazare |
| Big five = 90% of lip sync; jaw plus corners 50 to 60% | controls | Santos, Elver |
| Game speeds: walk 2, run 4 to 6, sprint 12 to 15 m/s | stylized player | Newman |
Playblast default scale 0.5: pass percent=100 | active view | Maya 2027 Help |
Quality gates
Measurable (A.run_gates(plan); pure logic A.evaluate_gates), for any jump, hop, landing or
weight shift in the plan: fps; heavy air g_ratio 0.8 to 1.25, hang excess at most 1 frame,
constant horizontal speed; catch (60%+ of the fall speed gone in 2 frames, decel shorter than
recovery), then a 3+ frame hold at the bottom; push fastest; heavy resistance beat of 3+ still
frames; every crouch: foot roll moves, spine lags the hips; touch-downs flat within 2 frames;
lift-off toe within 30 degrees of the takeoff spot; planted feet at most 0.2 cm/frame, penetration
under 0.5 cm; pre-landing stretch; no pops outside impacts; overlap lags; moving end hold; tell
under 90% after splining; no sub-frames; no layers at polish. [added] thresholds are toolkit
defaults. Visual: A.review_images(plan, out) (headless: curves.png stacked lanes,
tracks.png camera-space tracks with onion-skin poses); in the GUI A.playblast_pack() (shot
camera plus a no-lights silhouette) and a stepped-preview playblast. Read frames in time order;
write the first-view read before the numbers (critique.md).
Common mistakes
| Mistake | What it looks like | Fix |
|---|
| Splining before the poses are in | mush, arms tweened through the body | A.restore(snap), add breakdowns stepped |
| Even spacing, whole body on the same frames | floaty, "hitting poses" | favor neighbors, holds, offset_keys down the chains |
| First-pass anticipation kept | the crouch upstages the action | A.scale_curve(..., time=crouch) about the rest value |
| Torso locked to the hips, arms swung back in the crouch | stiff, generic anticipation | spine drags behind the hips, only the arms drop |
| Feet flat in the crouch, pointing forward at takeoff, slow to flatten on landing | weak compression, no push, slapping feet | foot roll; toe aimed back at the takeoff spot; flat within 1 to 2 frames |
| Stylized hang on a heavy body | floaty apex | ballistic_keys, flat apex, broken contact tangent |
| No resistance before the effort | styrofoam weight | 2+ frames of strain before the push |
| Symmetric dip, or a bounce straight out of the squash | light landing | sudden catch, bigger hold, one overshoot, slow recovery |
S or bare setKeyframe on a splined curve | neighbors reshape, holds drift | A.insert_key, then A.curve_diff |
| Spline through a hold | drifting hold | key_hold pillars, flat or plateau sides |
| Arcs checked on a 3D trail | clean trail, broken arc on screen | camera_track pixel tracks |
| Lip sync keyed on the sound | late at speed | per-sound lead, then a global shift |
Handoffs
Receives from scenario-maya-rigging and scenario-maya-deformation: the rig referenced (never
imported), validate(profile="rig") clean, the control list with IK/FK, space and foot roll
attributes, a range-of-motion playblast. Rig problems (knee pop, flipping space) go back as a note
with frames. Delivers shots to scenario-maya-lighting-rendering: a new saved version,
validate(profile="shot") clean, layers merged, helpers deleted (A.delete_helpers()), run_gates
JSON with no fail, the final playblast the agent looked at, what was not verified. Game clips to
scenario-maya-pipeline-scripting: skeleton baked per clip, clip names and ranges (bookmarks), root
rule (in place or root motion), loop seam and speed reports.
Maya 2027 notes
- Default tangent: Auto Span (Legacy) on one doc page, Clamped on another: query
keyTangent(q=True, g=True, itt=True). Lock/Free Tangent Weight is now Tangent Length;
Butterworth is Smooth Filter (Butterworth).
- Time Slider > Enable Stepped Preview;
A.stepped_preview() is the scripted twin. Dope Sheet Time
Snap is off since 2025.1 and scaleKey leaves sub-frames: A.snap_subframes().
- Ghost Editor and Motion Trail Editor sit under Visualize; ghosting a skinned mesh needs Cached
Playback (disabled by motion blur, Trax, classic dynamics).
- Playblast needs a model panel (GUI via mx_bridge); in mayapy use
review_images or Arnold stills.
- Sequencer (ex Camera Sequencer): lock every camera. HumanIK bakes from its own window, not Bake
Simulation. Time Editor clips never mix with animation layers. Blue Pencil replaced Grease Pencil.
References