A Blender addon for exporting skeletal animations to USDZ format, optimized for Apple Vision Pro and Reality Composer Pro.
Blender's built-in USD exporter has issues with skeletal animations:
- Multiple actions don't concatenate properly into a single timeline
- NLA tracks can interfere with export
- Animation bindings are sometimes missing in the output
This addon solves these problems by properly baking all your animations into a single timeline and exporting a clean USDZ that works reliably in visionOS apps.
- Visual Animation Manager — See all actions with frame counts and durations
- Selective Export — Choose which animations to include via checkboxes
- Animation Preview — Preview any animation directly in the viewport
- Two Export Modes — Baked (recommended) or NLA-based export
- Auto-Detection — Finds armatures automatically, even when a mesh is selected
- Animation Map Output — Generates JSON and/or text files with frame ranges
- Quick Re-Export — One-click re-export with previous settings
- Mesh Tools — Flip and recalculate normals for fixing inside-out models
- Axis Conversion — Automatic Z-up to Y-up conversion for visionOS
- Detailed Logging — See exactly what's happening during export
- Download
avp_animation_exporter_v3.py - Open Blender → Edit → Preferences → Add-ons
- Click Install... and select the downloaded file
- Enable the checkbox next to "AVP Animation Exporter Pro"
- Save preferences to keep it enabled
- Select your character mesh or armature
- Press N in the 3D Viewport to open the sidebar
- Click the AVP Export tab
- Click Refresh Actions to scan for animations
- Check/uncheck animations you want to export
- Click Export USD
You can also access the exporter via File → Export → USD for AVP (.usdz)
Shows the selected object, associated armature, active action, and mesh count.
Tools for fixing common mesh issues before export.
| Tool | Description |
|---|---|
| Flip Normals | Invert the direction of normals on selected faces |
| Recalculate Outside | Make normals face outward (standard for solid objects) |
| Recalculate Inside | Make normals face inward (for rooms/interiors) |
- In Edit Mode: affects only selected faces
- In Object Mode: affects all faces on the mesh
Tip: If your model appears inside-out or invisible in visionOS, try flipping or recalculating the normals.
Lists all detected actions with:
- Checkbox to include/exclude from export
- Animation name
- Frame count and duration
- Select All / Deselect All buttons
- Preview any animation by selecting it and clicking Preview Selected
- Loops the animation and sets the timeline to match
- Click Stop Preview when done
| Mode | Description | Best For |
|---|---|---|
| Baked Action | Bakes all animations into a single action with keyframes on every frame | Maximum compatibility, recommended for visionOS |
| NLA Track | Exports using NLA track structure | Preserving action organization |
| Setting | Description | Default |
|---|---|---|
| Frame Padding | Gap between animations in frames | 10 |
| Use Object Name | Name output file after the armature | On |
| Delete Temp Track | Clean up temporary data after export | On |
| Selected Only | Export only armature and its meshes | On |
| Export Materials | Include materials in USD | On |
| Export Textures | Include textures in USD | On |
| Export Shape Keys | Include blend shapes | On |
| Setting | Description |
|---|---|
| JSON Animation Map | Detailed JSON with frame ranges, durations, FPS |
| AnimRanges.txt | Human-readable text file with frame ranges |
Blender uses a Z-up coordinate system, while visionOS/Reality Composer Pro uses Y-up. This panel handles the conversion automatically.
| Setting | Description | Default |
|---|---|---|
| Convert Orientation | Enable coordinate system conversion | On |
| Forward Axis | The forward direction in the exported file | Z |
| Up Axis | The up direction in the exported file | Y |
Presets:
- visionOS — Sets Forward: Z, Up: Y (recommended for Apple Vision Pro)
- Blender Default — Disables conversion, keeps Z-up
Note: The defaults are already configured for visionOS. Most users won't need to change these settings unless importing assets from other software with different conventions.
Expandable panel showing export progress and any warnings/errors.
After export, you'll have:
MyCharacter.usdz # The animated model
MyCharacter_animmap.json # Machine-readable animation data
MyCharacter_AnimRanges.txt # Human-readable frame ranges
{
"fps": 24,
"padding": 10,
"count": 5,
"length": 500,
"duration": 20.83,
"export_mode": "BAKED",
"views": {
"Idle": {
"name": "Idle",
"start": 1,
"end": 101,
"length": 100,
"duration": 4.17
},
"Walk": {
"name": "Walk",
"start": 111,
"end": 151,
"length": 40,
"duration": 1.67
}
}
}Animation Ranges for Arm_Character
==================================================
FPS: 24
Total Frames: 500
Total Duration: 20.83s
Export Mode: BAKED
--------------------------------------------------
Idle: frames 1–101 (100f, 4.17s)
Walk: frames 111–151 (40f, 1.67s)
Run: frames 161–189 (28f, 1.17s)
Use the animation map to play specific animations by seeking to their frame range:
// Load your entity with the animation
let entity = try await Entity(named: "MyCharacter", in: realityKitBundle)
// Get animation resource
guard let animation = entity.availableAnimations.first else { return }
// Calculate time for "Walk" animation (starts at frame 111, 24 FPS)
let walkStartTime = 111.0 / 24.0 // 4.625 seconds
// Play from that point
var controller = entity.playAnimation(animation)
controller.time = walkStartTimestruct AnimationRange: Codable {
let name: String
let start: Int
let end: Int
let length: Int
let duration: Double
}
struct AnimationMap: Codable {
let fps: Double
let padding: Int
let count: Int
let length: Int
let duration: Double
let views: [String: AnimationRange]
}
// Load and parse
let mapURL = Bundle.main.url(forResource: "MyCharacter_animmap", withExtension: "json")!
let data = try Data(contentsOf: mapURL)
let animMap = try JSONDecoder().decode(AnimationMap.self, from: data)
// Get a specific animation's start time
let walkAnim = animMap.views["Walk"]!
let startTime = Double(walkAnim.start) / animMap.fps- Make sure your armature has actions in the Action Editor
- Actions shorter than the minimum frame length (default: 5) are skipped
- The baked action name is excluded from the list
- Use Baked Action mode (default)
- Ensure the armature is selected or a mesh with an armature modifier
- Check the log panel for errors
- Verify animations play correctly in Blender first
- Try increasing frame padding if animations blend together
- Check that bone names don't have special characters
- Enable Selected Only and make sure meshes are selected
- Or disable Selected Only to export everything
- The addon has fallbacks for different Blender versions
- If issues persist, try Blender 3.6 LTS or newer
- Blender 3.0 or newer (3.6 LTS recommended)
- No external dependencies for basic functionality
If animations still don't play after export (rare with Baked mode), enable Fix Animation Binding in settings. This requires the usd-core Python package:
# In Blender's Python console:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "usd-core"])MIT License — feel free to use, modify, and distribute.
Issues and pull requests welcome! Please include:
- Blender version
- Steps to reproduce
- Sample .blend file if possible
-
Mike Bundy — Main contributor X (@mikebundy) | LinkedIn
-
Andrew Edwards LinkedIn | Mastodon (@platformgoblin)
Thanks to the following people for their generous lessons on animations and Blender:
- Built for the Apple Vision Pro developer community
- Inspired by the challenges of getting skeletal animations into visionOS
- Thanks to everyone who reported issues and tested fixes
