
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
Professional-grade, framework-agnostic biomechanical animation SDK for sports analytics, rehabilitation apps, and motion capture systems
Professional-grade, framework-agnostic biomechanical animation engine for sports analytics, rehabilitation apps, and motion capture systems.
npm install posev
<script src="https://cdn.jsdelivr.net/npm/posev@1.0.0/dist/posev.umd.min.js"></script>
<script>
const { PoseVCore } = window.PoseV;
</script>
npm list posev
import { PoseVCore } from 'posev';
// 1. Create engine
const engine = new PoseVCore({
debug: true,
strictValidation: true,
});
// 2. Load movement data
await engine.load({
movement: './movements/squat.json',
rig: './rigs/standard-human.json',
});
// 3. Play animation
engine.play();
// 4. Listen to frame updates
engine.on('frameUpdate', (frame) => {
console.log(`Progress: ${(frame.progress * 100).toFixed(1)}%`);
console.log(`Current time: ${frame.currentTime.toFixed(2)}ms`);
// Update your UI or renderer here
renderPose(frame.pose);
});
// 5. Control playback
engine.pause();
engine.seek(0.5); // Seek to 50%
engine.setSpeed(0.5); // Slow motion
engine.play();
// Cleanup
engine.dispose();
<template>
<div class="posev-container">
<canvas ref="canvas" width="800" height="600"></canvas>
<div class="controls">
<button @click="togglePlayPause">
{{ isPlaying ? 'Pause' : 'Play' }}
</button>
<input
type="range"
min="0"
max="1"
:value="progress"
@input="seek"
/>
<span>{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { usePoseV } from 'posev';
const canvas = ref<HTMLCanvasElement>();
const { play, pause, seek, getState } = usePoseV({
movement: './movements/squat.json',
rig: './rigs/standard-human.json',
renderer: {
canvas: canvas.value,
width: 800,
height: 600,
},
autoplay: true,
});
const state = computed(() => getState());
const isPlaying = computed(() => state.value?.playback === 'playing');
const progress = computed(() => state.value?.progress ?? 0);
const currentTime = computed(() => state.value?.currentTime ?? 0);
const duration = computed(() => state.value?.duration ?? 0);
const togglePlayPause = () => {
isPlaying.value ? pause() : play();
};
const formatTime = (ms: number) => {
const seconds = (ms / 1000).toFixed(2);
return seconds;
};
</script>
<style scoped>
.posev-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
import { useEffect, useRef, useState } from 'react';
import { PoseVCore, PerformanceMonitor, ResourceManager } from 'posev';
export function PoseVPlayer({ movementUrl, rigUrl }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const engineRef = useRef<PoseVCore | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const [duration, setDuration] = useState(0);
useEffect(() => {
const perfMonitor = new PerformanceMonitor();
const resourceMgr = new ResourceManager();
const engine = new PoseVCore({
renderer: {
canvas: canvasRef.current!,
type: 'canvas2d',
},
perfMonitor,
resourceMgr,
});
engineRef.current = engine;
engine.load({ movement: movementUrl, rig: rigUrl }).then(() => {
setDuration(engine.getTimeline()?.duration ?? 0);
engine.on('frameUpdate', (frame) => {
setProgress(frame.progress);
});
});
return () => {
engine.dispose();
resourceMgr.cleanup();
};
}, [movementUrl, rigUrl]);
const togglePlayPause = () => {
if (isPlaying) {
engineRef.current?.pause();
} else {
engineRef.current?.play();
}
setIsPlaying(!isPlaying);
};
const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
const newProgress = parseFloat(e.target.value);
engineRef.current?.seek(newProgress);
};
return (
<div>
<canvas ref={canvasRef} width={800} height={600} />
<div>
<button onClick={togglePlayPause}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<input
type="range"
min="0"
max="1"
step="0.01"
value={progress}
onChange={handleSeek}
/>
<span>{(progress * 100).toFixed(1)}%</span>
</div>
</div>
);
}
Full Composition API support with lifecycle management:
import { usePoseV } from 'posev';
import { ref, onMounted } from 'vue';
const { play, pause, seek, getState } = usePoseV({
movement: 'squat.json',
rig: 'standard-human.json',
autoplay: true,
debug: false,
});
onMounted(() => {
play();
});
See Vue Integration Guide for details.
const { play, pause, seek } = usePoseV(config);
Works with any framework:
const engine = new PoseVCore(config);
await engine.load(manifest);
engine.play();
Main animation engine class.
new PoseVCore(config?: PoseVCoreConfig)
Config Options:
interface PoseVCoreConfig {
// Validation
strictValidation?: boolean; // Default: false
// Rendering
renderer?: RendererConfig; // Canvas/WebGL settings
// Production Hardening
errorHandler?: ErrorHandler; // Custom error handler
perfMonitor?: PerformanceMonitor; // Performance tracking
resourceMgr?: ResourceManager; // Memory management
// Debugging
debug?: boolean; // Enable debug mode
logger?: Logger; // Custom logger
}
// Asset Management
await engine.load(manifest: MovementManifest): Promise<void>
await engine.assets.preload(assets: AssetDef[]): Promise<void>
// Playback Control
engine.play(): void
engine.pause(): void
engine.stop(): void
engine.seek(progress: 0..1): void
engine.setSpeed(multiplier: number): void
// State Query
engine.getState(): PlaybackState
engine.getTimeline(): Timeline | null
engine.getPose(time?: number): Pose | null
engine.getProgress(): number
engine.getDuration(): number
// Events
engine.on(event: 'frameUpdate' | 'stateChange' | 'error', listener): Unsubscriber
engine.off(event, listener): void
engine.emit(event, data): void
// Lifecycle
engine.dispose(): void
// Frame update - fires every animation frame
engine.on('frameUpdate', (frame: FrameData) => {
// { currentTime, progress, pose, deltaTime }
});
// Playback state changed
engine.on('stateChange', (state: PlaybackState) => {
// { timeline, playback, speed, progress }
});
// Error occurred
engine.on('error', (error: PoseVError) => {
// { code, category, severity, message }
});
// Complete - animation finished
engine.on('complete', (stats: PlaybackStats) => {
// { totalTime, frameCount, avgFps }
});
Animation timeline with keyframes.
interface Timeline {
movement: Movement;
rig: Rig;
keyframes: Keyframe[];
duration: number; // Total duration in ms
fps: number; // Frames per second
interpolationMode: 'linear' | 'cubic';
// Query methods
getPoseAt(time: number): Pose;
getKeyframeAt(time: number): Keyframe;
getCurrentProgress(time: number): 0..1;
}
Skeletal pose data.
interface Pose {
joints: Map<string, Vector3>; // Joint positions
bones?: Map<string, Vector3>; // Bone vectors (optional)
timestamp: number;
getJoint(id: string): Vector3 | undefined;
hasJoint(id: string): boolean;
}
Movement definition (loaded from JSON).
interface Movement {
id: string;
name: string;
description?: string;
duration: number; // in milliseconds
fps: number;
keyframes: KeyframeData[];
// Validation
validate(): ValidationResult;
getKeyframeCount(): number;
getDurationSeconds(): number;
}
Skeletal rig definition.
interface Rig {
id: string;
name: string;
joints: JointDef[];
bones: BoneDef[];
// Query
getJoint(id: string): JointDef | undefined;
getParent(jointId: string): JointDef | undefined;
getChildren(jointId: string): JointDef[];
// Validation
validate(): ValidationResult;
validateBoneLengths(pose: Pose): BoneValidation;
validateAngles(pose: Pose): AngleValidation;
}
interface JointDef {
id: string;
name: string;
parent?: string;
constraints?: AngleConstraint[];
}
interface BoneDef {
id: string;
from: string;
to: string;
minLength: number;
maxLength: number;
idealLength: number;
}
interface PoseVPlugin {
// Metadata
id: string;
version: string;
dependencies?: string[];
// Lifecycle
install?(context: PluginContext): Promise<void>;
activate?(context: PluginContext): Promise<void>;
deactivate?(): Promise<void>;
uninstall?(): Promise<void>;
// Optional hook
onFrameUpdate?(frame: FrameData): void | Promise<void>;
onStateChange?(state: PlaybackState): void | Promise<void>;
onError?(error: PoseVError): boolean | Promise<boolean>; // Return true to suppress
}
interface PluginContext {
engine: PoseVCore;
timeline: Timeline;
rig: Rig;
renderer: Renderer;
errorHandler: ErrorHandler;
perfMonitor: PerformanceMonitor;
}
Create a gait analysis plugin:
import { PoseVPlugin, PluginContext, FrameData } from 'posev';
class GaitAnalysisPlugin implements PoseVPlugin {
id = 'gait-analysis';
version = '1.0.0';
private context: PluginContext | null = null;
private metrics = { stride: 0, cadence: 0, symmetry: 0 };
async install(context: PluginContext) {
this.context = context;
console.log('GaitAnalysisPlugin installed');
}
async activate() {
console.log('GaitAnalysisPlugin activated');
}
async deactivate() {
console.log('GaitAnalysisPlugin deactivated');
}
onFrameUpdate(frame: FrameData) {
// Analyze pose data
const pose = frame.pose;
// Calculate stride length
const leftFoot = pose.getJoint('left_foot');
const rightFoot = pose.getJoint('right_foot');
if (leftFoot && rightFoot) {
const distance = Math.hypot(
rightFoot.x - leftFoot.x,
rightFoot.z - leftFoot.z
);
this.metrics.stride = distance;
}
// Calculate cadence (steps per minute)
this.metrics.cadence = (this.metrics.stride / frame.deltaTime) * 60;
// Emit custom event or update UI
this.context?.engine.emit('gait:metrics', this.metrics);
}
getMetrics() {
return { ...this.metrics };
}
}
// Usage
const plugin = new GaitAnalysisPlugin();
engine.plugins.install(plugin);
engine.on('gait:metrics', (metrics) => {
console.log('Stride length:', metrics.stride.toFixed(2), 'cm');
console.log('Cadence:', metrics.cadence.toFixed(1), 'steps/min');
});
class AdvancedAnalysisPlugin implements PoseVPlugin {
id = 'advanced-analysis';
version = '1.0.0';
dependencies = ['gait-analysis']; // Requires GaitAnalysisPlugin
async install(context: PluginContext) {
// Install will fail if dependency not available
}
}
class RobustPlugin implements PoseVPlugin {
id = 'robust';
version = '1.0.0';
onFrameUpdate(frame: FrameData) {
try {
// Process frame
} catch (error) {
// Log but don't crash
console.error('Plugin error:', error);
}
}
// Return true to suppress error propagation
onError(error: PoseVError) {
if (error.code === 'P_5004') {
// Handle specific error
return true; // Suppress
}
return false; // Propagate
}
}
{
"version": "1.0.0",
"metadata": {
"id": "squat",
"name": "Bodyweight Squat",
"description": "Standard squat movement",
"author": "Motion Capture Studio",
"captureDate": "2024-11-23",
"fps": 60,
"duration": 2000,
"units": "centimeters"
},
"rig": {
"id": "standard-human",
"version": "1.0.0"
},
"keyframes": [
{
"time": 0,
"joints": {
"root": { "x": 0, "y": 0, "z": 0 },
"left_hip": { "x": -8, "y": 95, "z": 0 },
"left_knee": { "x": -8, "y": 52, "z": 0 },
"left_ankle": { "x": -8, "y": 2, "z": 0 },
"right_hip": { "x": 8, "y": 95, "z": 0 },
"right_knee": { "x": 8, "y": 52, "z": 0 },
"right_ankle": { "x": 8, "y": 2, "z": 0 }
}
},
{
"time": 1000,
"joints": {
"root": { "x": 0, "y": 0, "z": 0 },
"left_hip": { "x": -8, "y": 60, "z": 0 },
"left_knee": { "x": -8, "y": 20, "z": 0 },
"left_ankle": { "x": -8, "y": 2, "z": 0 },
"right_hip": { "x": 8, "y": 60, "z": 0 },
"right_knee": { "x": 8, "y": 20, "z": 0 },
"right_ankle": { "x": 8, "y": 2, "z": 0 }
}
},
{
"time": 2000,
"joints": {
"root": { "x": 0, "y": 0, "z": 0 },
"left_hip": { "x": -8, "y": 95, "z": 0 },
"left_knee": { "x": -8, "y": 52, "z": 0 },
"left_ankle": { "x": -8, "y": 2, "z": 0 },
"right_hip": { "x": 8, "y": 95, "z": 0 },
"right_knee": { "x": 8, "y": 52, "z": 0 },
"right_ankle": { "x": 8, "y": 2, "z": 0 }
}
}
],
"interpolation": "cubic",
"loops": true,
"tags": ["lower-body", "squat", "strength"]
}
interface MovementJSON {
// Version and metadata
version: '1.0.0';
metadata: {
id: string; // Unique identifier
name: string; // Human-readable name
description?: string;
author?: string;
captureDate?: string;
fps: number; // Frame rate (30-120)
duration: number; // Total duration in ms
units?: 'centimeters' | 'meters' | 'inches';
};
// Rig reference
rig: {
id: string;
version: string;
};
// Keyframes (minimum 2)
keyframes: Array<{
time: number; // Time in ms (0 to duration)
joints: {
[jointId: string]: { // One entry per joint in rig
x: number;
y: number;
z: number;
};
};
}>;
// Animation settings
interpolation?: 'linear' | 'cubic'; // Default: 'linear'
loops?: boolean; // Default: true
tags?: string[]; // Categorization
}
Time Values
Joint Coverage
Coordinate Ranges
Duration
import { MovementValidator } from 'posev';
const movementData = {
version: '1.0.0',
metadata: {
id: 'jump',
name: 'Standing Jump',
fps: 60,
duration: 1200,
},
rig: { id: 'standard-human', version: '1.0.0' },
keyframes: [
{ time: 0, joints: { /* ... */ } },
{ time: 600, joints: { /* ... */ } },
{ time: 1200, joints: { /* ... */ } },
],
};
// Validate before use
const validator = new MovementValidator();
const result = validator.validate(movementData);
if (result.valid) {
console.log('✅ Movement data is valid');
} else {
console.error('❌ Validation errors:', result.errors);
}
A rig defines the skeletal structure and constraints for animations. PoseV ships with predefined humanoid rigs and supports custom rigs.
The default rig with 17 joints and 16 bones:
Root (pelvis)
├── Spine
│ └── Chest
│ ├── Left Shoulder
│ │ └── Left Arm
│ │ └── Left Forearm
│ │ └── Left Hand
│ └── Right Shoulder
│ └── Right Arm
│ └── Right Forearm
│ └── Right Hand
├── Left Hip
│ └── Left Knee
│ └── Left Ankle
│ └── Left Foot
└── Right Hip
└── Right Knee
└── Right Ankle
└── Right Foot
Joint IDs:
root, spine, chest,
left_shoulder, left_arm, left_forearm, left_hand,
right_shoulder, right_arm, right_forearm, right_hand,
left_hip, left_knee, left_ankle, left_foot,
right_hip, right_knee, right_ankle, right_foot
import { StandardHumanRig, CompactHumanRig } from 'posev';
// Standard rig (17 joints, full detail)
const rig = new StandardHumanRig();
// Compact rig (11 joints, optimized)
const compactRig = new CompactHumanRig();
// Access rig data
const joints = rig.joints;
const bones = rig.bones;
// Query hierarchy
const parent = rig.getParent('left_knee'); // 'left_hip'
const children = rig.getChildren('spine'); // ['chest']
// Validation
const validation = rig.validate();
if (!validation.valid) {
console.error(validation.errors);
}
{
"version": "1.0.0",
"metadata": {
"id": "standard-human",
"name": "Standard Human Rig",
"description": "Biomechanically accurate humanoid rig",
"jointCount": 17
},
"joints": [
{
"id": "root",
"name": "Root/Pelvis",
"parent": null,
"constraints": [
{
"axis": "x",
"min": -30,
"max": 30,
"unit": "degrees"
}
]
},
{
"id": "left_knee",
"name": "Left Knee",
"parent": "left_hip",
"constraints": [
{
"axis": "z",
"min": 0,
"max": 140,
"unit": "degrees"
}
]
}
],
"bones": [
{
"id": "left_femur",
"from": "left_hip",
"to": "left_knee",
"minLength": 35,
"maxLength": 55,
"idealLength": 45,
"unit": "centimeters"
}
]
}
import { Rig, JointDef, BoneDef } from 'posev';
const customRig = new Rig({
id: 'quadruped',
name: 'Four-Legged Animal',
joints: [
{ id: 'root', name: 'Root', parent: null },
{ id: 'front_left_leg', name: 'Front Left Leg', parent: 'root' },
{ id: 'front_left_knee', name: 'Front Left Knee', parent: 'front_left_leg' },
{ id: 'front_left_foot', name: 'Front Left Foot', parent: 'front_left_knee' },
// ... more joints
],
bones: [
{
id: 'front_left_femur',
from: 'front_left_leg',
to: 'front_left_knee',
minLength: 10,
maxLength: 25,
idealLength: 17,
},
// ... more bones
],
});
// Use with movement data
const movement = await engine.load({
movement: './movements/walk.json',
rig: customRig,
});
const validation = rig.validate();
if (!validation.valid) {
console.error('Rig errors:');
validation.errors.forEach(error => {
console.error(` - ${error.joint}: ${error.message}`);
});
}
// Validate specific pose
const poseValidation = rig.validateBoneLengths(pose);
if (!poseValidation.valid) {
poseValidation.violations.forEach(v => {
console.warn(`Bone ${v.bone} length ${v.actual}cm exceeds max ${v.max}cm`);
});
}
// Validate angles
const angleValidation = rig.validateAngles(pose);
angleValidation.violations.forEach(v => {
console.warn(`Joint ${v.joint} angle ${v.actual}° exceeds max ${v.max}°`);
});
// Get all ancestors
const ancestors = rig.getAncestors('left_foot');
// → ['left_ankle', 'left_knee', 'left_hip', 'root']
// Get all descendants
const descendants = rig.getDescendants('root');
// → [all joints in tree]
// Check relationship
const isDescendant = rig.isDescendant('left_foot', 'root');
// → true
// Get bone connecting two joints
const bone = rig.getBone('left_hip', 'left_knee');
// → { id: 'left_femur', ... }
PoseV uses aggressive tree-shaking and minification:
ESM: 24.2 KB gzipped (79.6% compression)
UMD Min: 13.7 KB gzipped (73% compression)
Mobile-first conservative targets:
| Metric | Target | Status |
|---|---|---|
| Frame Time | <16.67ms (60 FPS) | ✅ |
| Memory | <50 MB | ✅ |
| Load Time | <5 seconds | ✅ |
| Max Keyframes | 10,000 | ✅ |
23 predefined error codes across 6 categories:
import { ERROR_CATALOG, ErrorHandler } from 'posev';
const handler = new ErrorHandler();
try {
await engine.load(manifest);
} catch (error) {
handler.handle(error);
if (error.retryable) {
// Implement exponential backoff retry
setTimeout(() => engine.load(manifest), 1000);
}
console.log(handler.getReport());
}
Real-time performance monitoring:
import { PerformanceMonitor } from 'posev';
const monitor = new PerformanceMonitor();
engine.on('frameUpdate', (frame) => {
monitor.recordMetric({
fps: 60,
frameTime: 16.7,
memoryUsage: 42,
cpuTime: 14.5,
renderTime: 2.2,
});
});
// Check for violations
const status = monitor.getBudgetStatus();
if (!status.compliant) {
console.warn('Performance degradation:', status.violations);
}
// Generate report
console.log(monitor.getReport());
See examples/rehab-app/ for a complete application with:
See examples/mocap-integration/ for mocap workflow:
See examples/sports-dashboard/ for analytics:
We welcome contributions! See CONTRIBUTING.md for guidelines.
# Install dependencies
npm install
# Run dev server
npm run dev
# Run tests
npm test
# Run tests in watch mode
npm test -- --watch
# Build for production
npm run build
# Run linter
npm run lint
# Type check
npm run type-check
Before opening an issue, please check:
git checkout -b feature/amazing-feature)npm test)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Priority bug fixes, custom integration, and SLA support available. Contact: enterprise@posev.ai
MIT © 2024 PoseV Contributors
Built with ❤️ for sports tech, rehabilitation, and motion analysis professionals.
FAQs
Professional-grade, framework-agnostic biomechanical animation SDK for sports analytics, rehabilitation apps, and motion capture systems
We found that posev demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.