create-character-3d
Generate a 3D character model using Meshy API (text-to-3D or image-to-3D), with bone mapping and animation import.
SKILL.mdAutoAnimation/.claude/skills/create-character-3d/SKILL.md
GitHubCreate 3D Character
Generate a 3D character model from text or an image using the Meshy API, with automatic bone mapping, skeleton retargeting, and animation import support.
Purpose
Create a rigged 3D character model (GLB format) that can be placed on the canvas, animated with imported or generated motions, and exported via Remotion. Supports text-to-3D and image-to-3D workflows through the Meshy API, with optional auto-rigging.
Workflow
Text-to-3D
- User provides a text description (e.g., "a cartoon robot with blue eyes")
- Client calls
startTextTo3D(request)which sends to/api/meshy/text-to-3d - Server proxies to Meshy API, returns a
taskId - Client polls
pollTask(taskId)until status isSUCCEEDED - Result includes
model_urls.glb-- the GLB download URL - GLB is loaded into Three.js, skeleton is extracted
Image-to-3D
- User provides an image (uploaded or generated)
- Client calls
startImageTo3D(request)which sends to/api/meshy/image-to-3d - Same polling and download flow as text-to-3D
Auto-Rigging
After generation, models can be auto-rigged:
- Client calls
startAutoRig({ modelUrl })->/api/meshy/auto-rig - Poll
pollRigTask(taskId)until complete - Result is a rigged GLB with skeleton hierarchy
Remeshing
High-poly models can be decimated:
- Client calls
startRemesh({ modelUrl, targetPolycount })->/api/meshy/remesh - Poll
pollRemeshTask(taskId)until complete - Result is a reduced-poly GLB (default: 30,000 polygons)
Skeleton Types and Bone Mapping
The system supports multiple skeleton naming conventions:
| Type | Source | Example Bones |
|---|---|---|
mixamo |
Mixamo characters | mixamorigHips, mixamorigSpine |
readyplayerme |
Ready Player Me | Hips, Spine |
smpl |
SMPL body model | Pelvis, L_Hip |
hunyuan |
HunyuanMotion output | pelvis, spine_01 |
biped |
Standard biped | Pelvis, Spine1 |
custom |
Unknown skeleton | User-mapped |
Bone mapping maps 20 standard bone names to actual skeleton bone names:
Pelvis, Spine1, Spine2, Spine3, Neck, Head,
L_Collar, L_Shoulder, L_Elbow, L_Wrist,
R_Collar, R_Shoulder, R_Elbow, R_Wrist,
L_Hip, L_Knee, L_Ankle, R_Hip, R_Knee, R_Ankle
The BoneMappingEditor component provides a visual UI for mapping bones. Skeleton type is auto-detected from bone names using heuristics in gltfUtils.ts.
3D Lip Sync
2D viseme sprites can be projected onto 3D character faces via VisemeFaceMapping:
- A textured plane is positioned relative to the Head bone
- Offset, scale, and rotation are configurable
- Cross-fade transitions between viseme sprites
- Auto-fit recalculates placement based on actual model geometry
Storage
3D character GLB blobs are stored in IndexedDB via character3dDB.ts (too large for Supabase). Character metadata (name, bone mapping, settings) is stored in the Zustand store and can be persisted to Supabase.
Key Files
| Purpose | Path |
|---|---|
| Meshy API client | src/services/meshyAPI.ts |
| 3D character types | src/types/character3d.ts |
| 3D character store | src/stores/use3DCharacterStore.ts |
| Saved 3D characters | src/stores/useSaved3DCharactersStore.ts |
| 3D animation store | src/stores/use3DAnimationStore.ts |
| GLTF utilities | src/services/gltfUtils.ts |
| FBX converter | src/services/fbxConverter.ts |
| Skeleton retarget | src/services/skeletonRetarget.ts |
| Bone mapping editor | src/components/panels/BoneMappingEditor.tsx |
| 3D character panel | src/components/panels/Character3DPanel.tsx |
| 3D renderer | src/components/canvas/Character3DRenderer.tsx |
| Remotion 3D layer | src/remotion/Remotion3DCharacter.tsx |
| 3D rig store | src/stores/use3DRigStore.ts |
| IndexedDB storage | src/services/character3dDB.ts |
| Server route | server/routes/meshy.ts |
Meshy API Request Types
interface MeshyTextTo3DRequest {
prompt: string
mode?: 'preview' | 'refine'
art_style?: string
negative_prompt?: string
}
interface MeshyImageTo3DRequest {
image_url: string
mode?: 'preview' | 'refine'
}
interface MeshyAutoRigRequest {
modelUrl: string
}
Polling Pattern
All Meshy operations are async with task-based polling:
const { taskId } = await startTextTo3D({ prompt: 'a cartoon robot' })
const result = await pollTaskUntilComplete(taskId, {
intervalMs: 5000, // Poll every 5 seconds
maxWaitMs: 600000, // 10 minute timeout
onProgress: (progress, status) => {
console.log(`${status}: ${progress}%`)
},
})
Common Issues
- Meshy API key missing: Requires
MESHY_API_KEYin server.env. All calls go through the Express proxy. - GLB loading fails: Some Meshy models may have unsupported extensions. Use
useSafeGLTFhook for error-tolerant loading. - Bone mapping wrong: Auto-detection may fail for non-standard skeletons. Use the
BoneMappingEditorto manually map bones. - Model too heavy: Use remeshing to reduce polygon count before rendering. Default target is 30,000 polygons.
- Animation retargeting mismatch: Cross-skeleton retargeting requires correct bone mapping on both source and target skeletons.
- Credit gating: Operations use credit types
meshy-text-to-3d,meshy-image-to-3d,meshy-auto-rig.
Example
Description: "A stylized knight character with silver armor and a red cape"
The workflow will:
- Call Meshy text-to-3D with the description
- Poll until the 3D model is generated (typically 1-3 minutes)
- Download the GLB file
- Auto-detect skeleton type and build bone mapping
- Store GLB blob in IndexedDB, metadata in store
- Render on canvas via Three.js with OrbitControls
- Optionally auto-rig if the model lacks a skeleton
- Import animations (walk, idle, run) from the motion library