// Character Consistency and Template System for Puppet Productions
// Provides size references, template prompts, and character trait management
export class CharacterConsistencyManager {
constructor() {
this.sizeReferences = this.initializeSizeReferences();
this.templatePrompts = this.initializeTemplatePrompts();
this.puppetTraitCategories = this.initializePuppetTraits();
}
// Standard size reference system recognized by AI generators
initializeSizeReferences() {
return {
// Puppet size categories with specific measurements
sizes: {
'tiny': {
description: 'Small hand puppet, child-sized',
measurements: '12-18 inches tall, hand-held scale',
referenceObjects: 'size of a large book, fits in adult hands',
headToBodyRatio: '1:2',
avgWeight: '8-12 ounces'
},
'small': {
description: 'Standard hand puppet, compact performer',
measurements: '18-24 inches tall, desktop scale',
referenceObjects: 'size of a laptop computer, tabletop height',
headToBodyRatio: '1:2.5',
avgWeight: '1-2 pounds'
},
'medium': {
description: 'Full-size puppet, standard performer',
measurements: '30-36 inches tall, waist-high scale',
referenceObjects: 'height of a kitchen counter, adult waist-high',
headToBodyRatio: '1:3',
avgWeight: '3-5 pounds'
},
'large': {
description: 'Stage puppet, prominent character',
measurements: '42-48 inches tall, chest-high scale',
referenceObjects: 'height of a dining table, adult chest-high',
headToBodyRatio: '1:3.5',
avgWeight: '6-10 pounds'
},
'giant': {
description: 'Feature puppet, dominant presence',
measurements: '54-60 inches tall, adult shoulder height',
referenceObjects: 'height of a standard door, adult shoulder-high',
headToBodyRatio: '1:4',
avgWeight: '12-20 pounds'
}
},
// Standard reference objects for scale consistency
referenceObjects: {
'coin': 'US quarter (24.26mm diameter)',
'hand': 'adult human hand (7.5 inches length)',
'smartphone': 'iPhone standard size (5.8 x 2.8 inches)',
'book': 'paperback book (7.5 x 4.25 inches)',
'mug': 'coffee mug (4 inches tall, 3.5 inches diameter)',
'ruler': '12-inch standard ruler',
'doorframe': 'standard door frame (80 inches tall, 36 inches wide)',
'table': 'dining table (30 inches height)',
'chair': 'standard chair (32 inches back height, 18 inches seat)'
},
// Resolution standards for different outputs
resolutions: {
'draft': '1024x1024px (1:1), 512x768px (2:3)',
'standard': '1536x1536px (1:1), 1024x1536px (2:3)',
'professional': '2048x2048px (1:1), 1365x2048px (2:3)',
'print': '3000x3000px (1:1), 2000x3000px (2:3)',
'cinema': '3840x2160px (16:9), 2560x1440px (16:9)'
}
};
}
// Comprehensive template prompt system
initializeTemplatePrompts() {
return {
// Base puppet construction template
basePuppetTemplate: {
core: 'realistic puppet character, professional Muppet-style construction',
framing: 'waist-up view, {VIEW_ANGLE}',
background: 'solid black contrasting background, studio lighting',
quality: 'high quality 4K puppet photography, sharp focus'
},
// Character creation templates
characterCreation: {
establishingShot: `{CHARACTER_SIZE} hand puppet character with visible control rods, {CHARACTER_DESCRIPTION},
{SKIN_TONE_ACCURATE} felt fabric skin matching reference person exactly, {HAIR_COLOR_ACCURATE} hair replicating reference person,
{FACIAL_FEATURES_ACCURATE} face shape and features proportioned to match reference person,
{DEFINING_FEATURES}, {CONSTRUCTION_DETAILS}, two-person operation capable with finger articulation,
front view facing camera, {EMOTION_STATE}, professional puppet photography,
solid black background, studio lighting, {SCALE_REFERENCE}`,
profileViews: `{CHARACTER_SIZE} hand puppet character with visible control rods, {CHARACTER_DESCRIPTION},
{SKIN_TONE_ACCURATE} felt fabric skin matching reference person exactly, {HAIR_COLOR_ACCURATE} hair color,
{FACIAL_FEATURES_ACCURATE} face structure maintaining reference person's proportions,
{DEFINING_FEATURES}, {CONSTRUCTION_DETAILS}, {VIEW_ANGLE},
neutral expression, consistent character design from front view, two-person operable,
solid black background, studio lighting, {SCALE_REFERENCE}`,
emotionalExpressions: `{CHARACTER_SIZE} hand puppet character with visible control rods, {CHARACTER_DESCRIPTION},
{SKIN_TONE_ACCURATE} felt fabric skin matching reference person exactly, {HAIR_COLOR_ACCURATE} hair,
{FACIAL_FEATURES_ACCURATE} facial features replicating reference person's structure,
{DEFINING_FEATURES}, {CONSTRUCTION_DETAILS}, front view facing camera,
{EMOTION_STATE} expression, {EMOTION_SPECIFIC_DETAILS}, individual finger articulation visible,
solid black background, studio lighting, {SCALE_REFERENCE}`
},
// Scene production templates
sceneProduction: {
characterInScene: `{CHARACTER_SIZE} puppet character named {CHARACTER_NAME},
{CHARACTER_DESCRIPTION}, {DEFINING_FEATURES}, {CONSTRUCTION_DETAILS},
{SCENE_POSITION}, {CHARACTER_ACTION}, {EMOTION_STATE},
{SCENE_DESCRIPTION}, {LIGHTING_SETUP}, {SCALE_REFERENCE}`,
multiCharacterScene: `Multiple puppet characters in scene: {CHARACTER_LIST},
each maintaining their established size ratios and features,
{SCENE_DESCRIPTION}, {CHARACTER_INTERACTIONS}, {LIGHTING_SETUP},
consistent scale relationships, {SCALE_REFERENCE}`,
characterAction: `{CHARACTER_SIZE} puppet character {CHARACTER_NAME},
{CHARACTER_DESCRIPTION}, {DEFINING_FEATURES}, {CONSTRUCTION_DETAILS},
performing action: {ACTION_DESCRIPTION}, {MOVEMENT_STYLE},
{EMOTION_STATE}, {SCENE_CONTEXT}, {SCALE_REFERENCE}`
},
// Consistency enforcement rules
consistencyRules: {
mandatoryElements: [
'hand puppet construction with visible control rods (NO string puppet/marionette)',
'accurate skin tone fabric matching reference person',
'hair color and style replicating reference person exactly',
'facial features proportioned to match reference person',
'felt/foam texture with professional puppet construction',
'mechanical mouth operation with hand control',
'individual finger articulation capability',
'two-person operation capability with rod controls',
'consistent character features across all views and expressions',
'solid black contrasting background',
'waist-up framing showing puppet construction',
'studio lighting setup',
'scale reference object present'
],
prohibitedElements: [
'string puppet/marionette construction',
'generic puppet skin color (must match reference person)',
'inaccurate hair color (must match reference exactly)',
'cartoon or animation style rendering',
'inconsistent scale or proportions',
'background elements that distract from puppet',
'natural human skin textures on puppet',
'unrealistic eye movement',
'clothing that doesn\'t reflect person\'s style',
'puppet hanging from strings or wires',
'overly simplified facial features'
]
}
};
}
// Puppet trait categories for database storage
initializePuppetTraits() {
return {
physicalTraits: {
size: ['tiny', 'small', 'medium', 'large', 'giant'],
construction: ['felt', 'foam', 'fur', 'fleece', 'mixed_materials'],
headShape: ['round', 'oval', 'angular', 'elongated', 'wide'],
eyeType: ['button', 'plastic', 'fabric', 'ping_pong', 'custom'],
mouthType: ['basic_opening', 'articulated_jaw', 'simple_slit', 'complex_mechanical'],
hairFur: ['synthetic_hair', 'wool', 'fur_fabric', 'yarn', 'feathers', 'bald'],
handType: ['simple_mittens', 'articulated_fingers', 'rod_controlled', 'glove_style']
},
movementTraits: {
eyeMovement: ['fixed_position', 'limited_side_to_side', 'full_mechanical', 'blink_capable'],
mouthMovement: ['simple_open_close', 'jaw_articulation', 'tongue_visible', 'teeth_visible'],
headMovement: ['basic_turn', 'full_rotation', 'tilt_capable', 'nod_shake'],
bodyMovement: ['static_pose', 'arm_articulation', 'torso_bend', 'full_puppeteer_control'],
handMovement: ['basic_positioning', 'finger_articulation', 'grip_capable', 'gesture_specific']
},
personalityTraits: {
energyLevel: ['low_energy', 'moderate', 'high_energy', 'hyperactive'],
movementStyle: ['bouncy_muppet', 'smooth_gliding', 'jerky_mechanical', 'flowing_graceful'],
voiceMatch: ['high_pitched', 'deep_resonant', 'raspy_textured', 'smooth_melodic'],
characterArchetype: ['hero', 'comic_relief', 'wise_mentor', 'villain', 'innocent', 'quirky']
}
};
}
// Generate size-specific prompt with reference objects
buildSizePrompt(characterSize, includeReference = true) {
const sizeData = this.sizeReferences.sizes[characterSize];
if (!sizeData) return '';
let prompt = `${sizeData.description}, ${sizeData.measurements}`;
if (includeReference) {
prompt += `, scale reference: ${sizeData.referenceObjects}`;
}
return prompt;
}
// Generate character-specific prompt from database traits
buildCharacterPrompt(characterTraits, promptType, templateVariables = {}) {
const template = this.templatePrompts.characterCreation[promptType];
if (!template) throw new Error(`Unknown prompt type: ${promptType}`);
// Build character description from traits
const description = this.buildCharacterDescription(characterTraits);
const definingFeatures = this.buildDefiningFeatures(characterTraits);
const constructionDetails = this.buildConstructionDetails(characterTraits);
const scaleReference = this.buildSizePrompt(characterTraits.size || 'medium');
// Build enhanced feature variables for accurate replication
const skinToneAccurate = this.extractSkinToneDetails(characterTraits);
const hairColorAccurate = this.extractHairDetails(characterTraits);
const facialFeaturesAccurate = this.extractFacialFeatures(characterTraits);
// Replace template variables including enhanced physical features
let prompt = template
.replace('{CHARACTER_SIZE}', this.buildSizePrompt(characterTraits.size || 'medium', false))
.replace('{CHARACTER_DESCRIPTION}', description)
.replace('{DEFINING_FEATURES}', definingFeatures)
.replace('{CONSTRUCTION_DETAILS}', constructionDetails)
.replace('{SCALE_REFERENCE}', scaleReference)
.replace('{SKIN_TONE_ACCURATE}', skinToneAccurate)
.replace('{HAIR_COLOR_ACCURATE}', hairColorAccurate)
.replace('{FACIAL_FEATURES_ACCURATE}', facialFeaturesAccurate);
// Replace any additional variables
Object.entries(templateVariables).forEach(([key, value]) => {
prompt = prompt.replace(`{${key}}`, value);
});
// Add consistency rules
const mandatoryElements = this.templatePrompts.consistencyRules.mandatoryElements.join(', ');
const negativeElements = this.templatePrompts.consistencyRules.prohibitedElements.join(', ');
return {
positive: `${prompt}, ${mandatoryElements}`,
negative: negativeElements
};
}
// Build detailed character description from traits
buildCharacterDescription(traits) {
const parts = [];
// Enhanced physical replication from Claude analysis
if (traits.physical_replication?.build_type) parts.push(traits.physical_replication.build_type);
if (traits.age) parts.push(`${traits.age} years old`);
if (traits.gender) parts.push(traits.gender);
// Legacy support
if (traits.species) parts.push(traits.species);
if (traits.primaryColor) parts.push(`primary color ${traits.primaryColor}`);
if (traits.secondaryColor) parts.push(`secondary color ${traits.secondaryColor}`);
return parts.length > 0 ? parts.join(', ') : 'puppet character';
}
// Build defining physical features
buildDefiningFeatures(traits) {
const features = [];
// Enhanced physical replication features from Claude analysis
if (traits.physical_replication?.eye_characteristics) {
features.push(traits.physical_replication.eye_characteristics);
}
if (traits.physical_replication?.facial_structure) {
features.push(traits.physical_replication.facial_structure);
}
if (traits.physical_replication?.distinctive_features?.length > 0) {
features.push(...traits.physical_replication.distinctive_features);
}
// Legacy support
if (traits.eyeColor) features.push(`${traits.eyeColor} eyes`);
if (traits.eyeType) features.push(`${traits.eyeType} eye construction`);
if (traits.hairFur) features.push(`${traits.hairFur} hair/fur`);
if (traits.specialMarkings) features.push(traits.specialMarkings);
if (traits.clothing) features.push(`wearing ${traits.clothing}`);
return features.length > 0 ? features.join(', ') : 'standard puppet features';
}
// Build construction-specific details
buildConstructionDetails(traits) {
const construction = [];
// Enhanced puppet construction from Claude analysis
if (traits.puppet_construction_specs?.puppet_type) {
construction.push(traits.puppet_construction_specs.puppet_type);
}
if (traits.puppet_construction_specs?.hand_operation) {
construction.push(traits.puppet_construction_specs.hand_operation);
}
if (traits.puppet_construction_specs?.material_specifications) {
construction.push(traits.puppet_construction_specs.material_specifications);
}
if (traits.puppet_construction_specs?.articulation_points?.length > 0) {
construction.push(`articulation: ${traits.puppet_construction_specs.articulation_points.join(', ')}`);
}
// Legacy support
if (traits.construction) construction.push(`${traits.construction} puppet construction`);
if (traits.mouthType) construction.push(`${traits.mouthType} mouth mechanism`);
if (traits.handType) construction.push(`${traits.handType} hand design`);
if (traits.movementStyle) construction.push(`${traits.movementStyle} movement style`);
return construction.length > 0 ? construction.join(', ') : 'standard hand puppet with rod controls';
}
// Extract accurate skin tone details from character traits
extractSkinToneDetails(traits) {
// First priority: Enhanced physical replication data from Claude
if (traits.physical_replication?.skin_tone) {
return traits.physical_replication.skin_tone + ' fabric puppet skin';
}
// Legacy fallback: physical_consistency schema
if (traits.physical_consistency?.skin_tone) {
return traits.physical_consistency.skin_tone + ' fabric puppet skin';
}
// Basic fallback: use primary color if available
if (traits.primaryColor) {
return `${traits.primaryColor} puppet skin`;
}
return 'standard felt puppet skin tone';
}
// Extract accurate hair details from character traits
extractHairDetails(traits) {
// First priority: Enhanced physical replication data from Claude
if (traits.physical_replication?.hair_replication) {
return traits.physical_replication.hair_replication + ' synthetic puppet hair';
}
// Legacy fallback: physical_consistency schema
if (traits.physical_consistency?.hair_details) {
return traits.physical_consistency.hair_details + ' puppet hair';
}
// Basic fallback: use hair/fur trait
if (traits.hairFur) {
return `${traits.hairFur} puppet hair`;
}
return 'standard synthetic puppet hair';
}
// Extract accurate facial features from character traits
extractFacialFeatures(traits) {
// First priority: Enhanced physical replication data from Claude
if (traits.physical_replication?.facial_structure) {
return traits.physical_replication.facial_structure + ' replicated in puppet form';
}
// Legacy fallback: physical_consistency schema
if (traits.physical_consistency?.facial_features) {
return traits.physical_consistency.facial_features + ' puppet construction';
}
// Basic fallback: build from individual traits
const features = [];
if (traits.eyeColor) features.push(`${traits.eyeColor} puppet eyes`);
if (traits.eyeType) features.push(`${traits.eyeType} eye construction`);
return features.length > 0 ? features.join(', ') : 'standard puppet facial proportions';
}
// Generate scene prompt maintaining character consistency
buildScenePrompt(characters, sceneDescription, templateVariables = {}) {
if (characters.length === 1) {
const character = characters[0];
const template = this.templatePrompts.sceneProduction.characterInScene;
let prompt = template
.replace('{CHARACTER_SIZE}', this.buildSizePrompt(character.traits.size || 'medium', false))
.replace('{CHARACTER_NAME}', character.name)
.replace('{CHARACTER_DESCRIPTION}', this.buildCharacterDescription(character.traits))
.replace('{DEFINING_FEATURES}', this.buildDefiningFeatures(character.traits))
.replace('{CONSTRUCTION_DETAILS}', this.buildConstructionDetails(character.traits))
.replace('{SCENE_DESCRIPTION}', sceneDescription)
.replace('{SCALE_REFERENCE}', this.buildSizePrompt(character.traits.size || 'medium'));
// Replace additional variables
Object.entries(templateVariables).forEach(([key, value]) => {
prompt = prompt.replace(`{${key}}`, value);
});
return prompt;
} else {
// Multi-character scene logic
const characterList = characters.map(char =>
`${char.name} (${this.buildSizePrompt(char.traits.size || 'medium', false)})`
).join(', ');
const template = this.templatePrompts.sceneProduction.multiCharacterScene;
let prompt = template
.replace('{CHARACTER_LIST}', characterList)
.replace('{SCENE_DESCRIPTION}', sceneDescription);
Object.entries(templateVariables).forEach(([key, value]) => {
prompt = prompt.replace(`{${key}}`, value);
});
return prompt;
}
}
// Validate prompt consistency
validatePrompt(prompt) {
const required = this.templatePrompts.consistencyRules.mandatoryElements;
const prohibited = this.templatePrompts.consistencyRules.prohibitedElements;
const validation = {
isValid: true,
missing: [],
violations: []
};
// Check for required elements
required.forEach(element => {
if (!prompt.toLowerCase().includes(element.toLowerCase())) {
validation.missing.push(element);
validation.isValid = false;
}
});
// Check for prohibited elements
prohibited.forEach(element => {
if (prompt.toLowerCase().includes(element.toLowerCase())) {
validation.violations.push(element);
validation.isValid = false;
}
});
return validation;
}
}