| name | movement-notation-systems |
| description | Designs systems for encoding, scoring, and generating choreographic movement using Laban notation, computational geometry, and procedural animation principles. |
| license | MIT |
Movement Notation Systems
This skill provides guidance for creating systems that encode, analyze, and generate human movement for choreography, animation, and movement analysis.
Core Competencies
- Movement Notation: Labanotation, Benesh, Motif notation
- Computational Geometry: Skeletal representation, joint angles
- Procedural Animation: Rule-based movement generation
- Effort-Shape Analysis: Laban Movement Analysis (LMA)
- Temporal Structures: Rhythm, phrasing, dynamics
Movement Notation Fundamentals
The Challenge of Movement
Movement is inherently multidimensional:
- 3D spatial paths
- Temporal evolution
- Body part coordination
- Qualitative dynamics (effort)
- Relational context (other bodies, objects, space)
Major Notation Systems
| System | Strengths | Use Cases |
|---|
| Labanotation | Complete, precise | Archival, reconstruction |
| Benesh | Compact, visual | Ballet, therapy |
| Motif | Abstract, readable | Teaching, analysis |
| Motion Capture | Exact coordinates | Animation, research |
Laban Movement Analysis Framework
Body Component
What body parts are moving:
Body Organization:
├── Core-Distal (center outward)
├── Head-Tail (spinal connection)
├── Upper-Lower (horizontal division)
├── Body-Half (left-right)
└── Cross-Lateral (diagonal connections)
Body Parts Hierarchy:
Center (pelvis)
├── Torso (spine, chest)
│ ├── Head
│ ├── Shoulders
│ └── Arms → Elbows → Hands → Fingers
└── Hips
└── Legs → Knees → Feet → Toes
Space Component
Where the body moves:
class KinesphereModel:
"""The reachable space around the body"""
DIMENSIONS = {
'vertical': {'up', 'down'},
'horizontal': {'left', 'right'},
'sagittal': {'forward', 'backward'}
}
LEVELS = ['low', 'middle', 'high']
DIRECTION_SYMBOLS = {
'place_high': (0, 1, 0),
'place_middle': (0, 0, 0),
'place_low': (0, -1, 0),
'forward_high': (0, 1, 1),
'forward_middle': (0, 0, 1),
'forward_low': (0, -1, 1),
}
SCALES = {
'near': 0.3,
'mid': 0.6,
'far': 1.0
}
Effort Component
How movement is performed (qualitative dynamics):
class EffortFactors:
"""Laban Effort qualities"""
FACTORS = {
'weight': {
'light': {'sensation': 'buoyant', 'value': -1},
'strong': {'sensation': 'powerful', 'value': 1}
},
'time': {
'sustained': {'sensation': 'leisurely', 'value': -1},
'quick': {'sensation': 'urgent', 'value': 1}
},
'space': {
'indirect': {'sensation': 'flexible', 'value': -1},
'direct': {'sensation': 'focused', 'value': 1}
},
'flow': {
'free': {'sensation': 'fluent', 'value': -1},
'bound': {'sensation': 'controlled', 'value': 1}
}
}
ACTIONS = {
'punch': {'weight': , : , : },
: {: , : , : },
: {: , : , : },
: {: , : , : },
: {: , : , : },
: {: , : , : },
: {: , : , : },
: {: , : , : }
}
Shape Component
How the body changes form:
class ShapeQualities:
"""Body shape changes"""
MODES = {
'shape_flow': {
'description': 'Internal shaping, self-oriented',
'examples': ['breathing', 'growing/shrinking']
},
'directional': {
'description': 'Bridge to environment',
'subtypes': ['spoke-like', 'arc-like']
},
'carving': {
'description': 'Sculpting 3D space',
'relationship': 'Interacting with environment'
}
}
AFFINITIES = {
'rising': {'effort': 'light', 'direction': 'up'},
'sinking': {'effort': 'strong', 'direction': 'down'},
'spreading': {'effort': 'indirect', 'direction': 'horizontal'},
'enclosing': {'effort': 'direct', 'direction': 'in'},
'advancing': {'effort': 'sustained', 'direction': 'forward'},
'retreating': {'effort': 'quick', : }
}
Computational Movement Representation
Skeletal Data Structure
class Skeleton:
"""Hierarchical skeletal representation"""
def __init__(self):
self.joints = {
'pelvis': Joint('pelvis', parent=None),
'spine': Joint('spine', parent='pelvis'),
'chest': Joint('chest', parent='spine'),
'neck': Joint('neck', parent='chest'),
'head': Joint('head', parent='neck'),
'l_shoulder': Joint('l_shoulder', parent='chest'),
'l_elbow': Joint('l_elbow', parent='l_shoulder'),
'l_wrist': Joint('l_wrist', parent='l_elbow'),
'r_shoulder': Joint('r_shoulder', parent='chest'),
}
def get_world_position(self, joint_name):
"""Compute global position from local transforms"""
joint = self.joints[joint_name]
position = joint.local_position
current = joint
while current.parent:
parent = self.joints[current.parent]
position = parent.rotation.apply(position) + parent.local_position
current = parent
return position
():
angles = {}
name, joint .joints.items():
joint.parent:
angles[name] = joint.rotation.as_euler()
angles
:
():
.name = name
.parent = parent
.local_position = np.array([, , ])
.rotation = Rotation.identity()
.constraints = {}
Motion Trajectory
class MotionTrajectory:
"""Temporal sequence of poses"""
def __init__(self, fps=30):
self.fps = fps
self.frames = []
self.annotations = []
def duration(self):
return len(self.frames) / self.fps
def get_velocity(self, joint_name, frame_idx):
"""Compute instantaneous velocity"""
if frame_idx < 1:
return np.zeros(3)
pos_current = self.frames[frame_idx].get_world_position(joint_name)
pos_prev = self.frames[frame_idx - 1].get_world_position(joint_name)
return (pos_current - pos_prev) * self.fps
def extract_effort_features(self, joint_name, window=10):
"""Estimate Laban Effort qualities from motion"""
features = {
'weight': self._compute_acceleration_magnitude(joint_name, window),
'time': self._compute_temporal_change_rate(joint_name, window),
'space': self._compute_path_directness(joint_name, window),
: ._compute_flow_continuity(joint_name, window)
}
features
Procedural Movement Generation
Rule-Based Choreography
class ChoreographyGenerator:
"""Generate movement sequences from rules"""
def __init__(self):
self.vocabulary = self._load_movement_vocabulary()
self.grammar = self._load_grammar_rules()
def generate_phrase(self, theme, duration_beats=16):
"""Generate choreographic phrase"""
motif = self._select_motif(theme)
phrase = [motif]
current_beats = motif.duration_beats
while current_beats < duration_beats:
if random.random() < 0.3:
variation = self._vary_motif(phrase[-1])
phrase.append(variation)
elif random.random() < 0.5:
contrast = self._generate_contrast(phrase[-1])
phrase.append(contrast)
else:
transition = self._smooth_transition(phrase[-1])
phrase.append(transition)
current_beats += phrase[-1].duration_beats
return phrase
def _vary_motif(self, motif):
variations = [
._change_level,
._mirror,
._change_size,
._change_tempo,
._change_direction,
._fragment,
._extend,
]
random.choice(variations)(motif)
Effort-Driven Animation
class EffortAnimator:
"""Generate movement with specified Effort qualities"""
def animate_action(self, skeleton, target_position, effort_state):
"""Move body part with specified Effort"""
if effort_state['time'] == 'quick':
duration = 0.3
ease_type = 'exponential'
else:
duration = 1.2
ease_type = 'sine'
if effort_state['weight'] == 'strong':
acceleration_curve = self._strong_curve()
else:
acceleration_curve = self._light_curve()
if effort_state['space'] == 'direct':
path = self._linear_path(skeleton.current, target_position)
else:
path = self._curved_path(skeleton.current, target_position)
if effort_state['flow'] == 'bound':
path = self._add_micro_pauses(path)
return self._create_animation(
path=path,
duration=duration,
acceleration=acceleration_curve
)
Spatial Pattern Generation
class FloorPatternGenerator:
"""Generate spatial pathways"""
def generate_path(self, pattern_type, space_bounds, duration):
"""Generate floor pattern"""
patterns = {
'circular': self._circular_path,
'spiral': self._spiral_path,
'figure_eight': self._figure_eight,
'diagonal_cross': self._diagonal_cross,
'zigzag': self._zigzag_path,
'random_walk': self._random_walk
}
generator = patterns.get(pattern_type, self._random_walk)
return generator(space_bounds, duration)
def _spiral_path(self, bounds, duration, turns=3):
"""Generate spiral floor pattern"""
center = bounds.center
max_radius = min(bounds.width, bounds.height) / 2
points = []
num_points = int(duration * 30)
for i in range(num_points):
t = i / num_points
angle = t * turns * 2 * np.pi
radius = max_radius * (1 - t)
x = center[0] + radius * np.cos(angle)
y = center[1] + radius * np.sin(angle)
points.append((x, y, t * duration))
return points
Notation Output
Textual Notation Format
class MovementScore:
"""Text-based movement score"""
def to_notation(self, phrase):
"""Convert phrase to readable notation"""
score = []
for movement in phrase:
notation = {
'beat': movement.start_beat,
'duration': movement.duration_beats,
'body': self._notate_body(movement),
'space': self._notate_space(movement),
'effort': self._notate_effort(movement),
'description': movement.description
}
score.append(notation)
return score
def _notate_effort(self, movement):
"""Effort notation symbols"""
symbols = {
('strong', 'quick', 'direct'): '⚡',
('light', 'sustained', 'indirect'): '☁️',
('strong', 'sustained', 'indirect'): '🌀',
}
effort_tuple = (
movement.effort['weight'],
movement.effort['time'],
movement.effort['space']
)
return symbols.get(effort_tuple, )
Visual Score Representation
Time →
| 0 | 1 | 2 | 3 | 4 |
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ ◊ HEAD │ │ ○ │ │ │
│ ═ ARMS │ ═══════│════ │ ═════ │═════ │
│ ║ TORSO │ ║ │ ║║ │ ║ │ ║║║ │
│ ‖ LEGS │ ‖‖ │ ‖ ‖ │ ‖‖ │ ‖ ‖ │
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ Level: │ High │ Mid │ Low │ Mid │
│ Effort: │ ⚡ │ ☁️ │ 🌀 │ ⚡ │
└─────────┴─────────┴─────────┴─────────┴─────────┘
Best Practices
Encoding Principles
- Preserve intent: Capture what the choreographer wants, not just positions
- Layer information: Structure > Shape > Dynamics
- Allow interpretation: Don't over-specify unless needed
- Support variation: Enable controlled improvisation
Analysis Guidelines
- Extract both quantitative (positions) and qualitative (effort) features
- Consider cultural and stylistic context
- Validate with practitioners
References
references/labanotation-symbols.md - Symbol reference for Labanotation
references/lma-glossary.md - Laban Movement Analysis terminology
references/motion-capture-formats.md - Common mocap data formats