Retarget to any character
Apply motions generated with Uthana to your custom 3D characters. Retargeting automatically adapts animations to work with different character skeletons and proportions.
Overview
When you generate a motion with Uthana, it's created for a specific character skeleton. Retargeting allows you to apply that same motion to any character you've uploaded, automatically adapting the animation to match different bone structures and proportions.
How retargeting works
- Generate or select a motion: Create a motion using text-to-motion or video-to-motion, or use an existing motion
- Choose your character: Select the character you want to apply the motion to
- Download the retargeted motion: The motion is automatically retargeted when you download it with a specific character ID
Step-by-step tutorial
Step 1: Upload your character
First, upload your custom character model. Uthana will automatically rig it if needed.
See the Auto-rig / add a character guide for detailed instructions on uploading characters, including supported formats, auto-rigging options, and code examples for all languages.
After uploading, save the character ID from the response. You'll need it for retargeting motions.
Step 2: Generate a motion
Generate a motion using Text to motion or Video to motion. Save the motion ID from the response—you'll need it for downloading the retargeted motion.
Step 3: Download retargeted motion
When you download a motion with a specific character ID, Uthana automatically retargets it to that character's skeleton.
- Shell
- Python
- TypeScript
- C#
# Download retargeted motion as FBX (filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.fbx" \
-u $API_KEY: \
-o retargeted-motion.fbx
# Download retargeted motion as GLB (filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.glb" \
-u $API_KEY: \
-o retargeted-motion.glb
# Download retargeted motion as FBX (filename is customizable)
fbx_url = f'https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.fbx'
response = requests.get(fbx_url, auth=(API_KEY, ''))
with open('retargeted-motion.fbx', 'wb') as f:
f.write(response.content)
# Download retargeted motion as GLB (filename is customizable)
glb_url = f'https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.glb'
response = requests.get(glb_url, auth=(API_KEY, ''))
with open('retargeted-motion.glb', 'wb') as f:
f.write(response.content)
async function downloadRetargetedMotion(characterId: string, motionId: string, format: "fbx" | "glb", filename: string = "motion") {
const url = `https://uthana.com/motion/file/motion_viewer/${characterId}/${motionId}/${filename}.${format}`;
const response = await fetch(url, {
headers: {
Authorization: `Basic ${btoa(`${API_KEY}:`)}`,
},
});
if (!response.ok) {
throw new Error(`Failed to download motion: ${response.statusText}`);
}
const blob = await response.blob();
return blob;
}
// Usage
const fbxBlob = await downloadRetargetedMotion(CHARACTER_ID, MOTION_ID, "fbx");
const glbBlob = await downloadRetargetedMotion(CHARACTER_ID, MOTION_ID, "glb");
public async Task<byte[]> DownloadRetargetedMotionAsync(string characterId, string motionId, string format = "fbx", string filename = "motion")
{
var downloadUrl = $"https://uthana.com/motion/file/motion_viewer/{characterId}/{motionId}/{filename}.{format}";
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
// Usage
var fbxData = await DownloadRetargetedMotionAsync(characterId, motionId, "fbx");
var glbData = await DownloadRetargetedMotionAsync(characterId, motionId, "glb");
Downloading motion-only files
For animation data without the character mesh (useful for applying to characters in your game engine), download the motion-only GLB:
- Shell
- Python
- TypeScript
- C#
# Download motion-only GLB (animation data without character mesh, filename is customizable)
curl -L "https://uthana.com/motion/animation/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.glb" \
-u $API_KEY: \
-o motion-only.glb
# Download motion-only GLB (filename is customizable)
motion_only_url = f'https://uthana.com/motion/animation/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.glb'
response = requests.get(motion_only_url, auth=(API_KEY, ''))
with open('motion-only.glb', 'wb') as f:
f.write(response.content)
async function downloadMotionOnlyGlb(characterId: string, motionId: string) {
const url = `https://uthana.com/motion/animation/motion_viewer/${characterId}/${motionId}/glb/${characterId}-${motionId}.glb`;
const response = await fetch(url, {
headers: {
Authorization: `Basic ${btoa(`${API_KEY}:`)}`,
},
});
if (!response.ok) {
throw new Error(`Failed to download motion-only GLB: ${response.statusText}`);
}
const blob = await response.blob();
return blob;
}
// Usage
const motionOnlyBlob = await downloadMotionOnlyGlb(CHARACTER_ID, MOTION_ID);
public async Task<byte[]> DownloadMotionOnlyGlbAsync(string characterId, string motionId, string filename = "motion")
{
var downloadUrl = $"https://uthana.com/motion/animation/motion_viewer/{characterId}/{motionId}/{filename}.glb";
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
// Usage
var motionOnlyData = await DownloadMotionOnlyGlbAsync(characterId, motionId);
Next steps
- Learn about Text to motion for generating animations
- Explore Asset management for managing your characters and motions
- Check the API reference for complete schema documentation