The
macro.json file defines MakeHuman2's
macro target system - a sophisticated mechanism for applying complex combinations of morphing targets based on hierarchical slider interactions. Macro targets allow a single slider (like "Gender" or "Age") to automatically calculate and apply dozens or even hundreds of underlying targets with appropriate weights.
The macro system solves the problem of
interdependent character attributes when you change gender, age, muscle, and weight, the system needs to apply different combinations of targets that work together harmoniously. The macro system calculates these combinations automatically.
File Locations and loading order
Per mesh there might be 3 different target folders, two of them can be used for
macro.json file.
| User constant targets |
<UserDataDir>/data/contarget/<basename>/macro.json |
This method is used to replace the read-only system-targets, this is used in case of a complete new base in the user folders (the system-targets are read-only. Especially when a mesh is downloaded as a packet. macro.json must be manually created. |
| System targets |
<SystemDir>/data/target/<basename>/macro.json |
System targets are supplied from MakeHuman team by installation. macro.json is part of the installation. |
<basename> is the base mesh identifier (e.g., hm08, mh2bot)
To conclude: If contarget/<basename>/macro.json exists, the system file is ignored. Otherwise, system files are scanned.
File Format
The
macro.json file consists of two main sections:
{
"components": {
"component-name": {
"values": ["value1", "value2", "value3"],
"pattern": "target/path/prefix",
"steps": [0.0, 0.5, 1.0],
"sum": ["Slider1", "Slider2", "Slider3"]
}
},
"macrodef": [
{
"name": "macro-name",
"comp": ["component1", "component2"],
"folder": "target_folder",
"targets": [
{"name": "calculated-name", "t": "actual-target-file"}
]
}
]
}
Components Section
The
components object defines reusable building blocks that describe how slider values map to target naming patterns. Each component represents a character dimension (gender, age, muscle, etc.).
The component structure is:
"component-name": {
"values": ["label1", "label2", "label3"],
"pattern": "target-path-prefix",
"steps": [0.0, 0.5, 1.0],
"sum": ["TargetName1", "TargetName2"]
}
Property Reference
| values arrays of strings, required |
List of naming labels for this component's states, it is used to construct target filenames.
Number of values determines how many states the component has
|
Examples:
["female", "male"]
["minmuscle", "averagemuscle", "maxmuscle"]
|
| pattern string, required |
Path prefix to the target controlling this component, which must match a target defined in modelling.json. It is used to read current slider values.
|
Examples:
"macrodetails/Gender"
"macrodetails-universal/Muscle"
|
| steps array of numbers, optional |
Defines breakpoints on the slider (0.0 to 1.0 range). It is used for stepped interpolation between discrete states.The array length must equal values array length. When present, it creates interpolation between adjacent steps.
|
Example:
[0.0, 0.5, 1.0] means value1 at 0%, value2 at 50%, value3 at 100%
|
| sum array of strings, optional |
Used for barycentric (3-way) sliders instead of stepped sliders. Lists the target names whose barycentric values sum to determine weights. It is mutually exclusive with steps - use one or the other. It must have exactly 3 entries for barycentric interpolation.
|
Example:
["African", "Asian", "Caucasian"]
|
Component Types
Stepped component (2 states), example: Gender:
"gender": {
"values": ["female", "male"],
"steps": [0.0, 1.0],
"pattern": "macrodetails/Gender"
}
How it works:
- Slider at 0.0 → 100% female, 0% male
- Slider at 0.5 → 50% female, 50% male
- Slider at 1.0 → 0% female, 100% male
- Linear interpolation between the two states
Stepped component (3 states), example: Muscle:
"muscle": {
"values": ["minmuscle", "averagemuscle", "maxmuscle"],
"steps": [0.0, 0.5, 1.0],
"pattern": "macrodetails-universal/Muscle"
}
How it works:
- Slider 0.0 to 0.5 → interpolate between minmuscle and averagemuscle
- Slider 0.5 to 1.0 → interpolate between averagemuscle and maxmuscle
- At exactly 0.5 → 100% averagemuscle
Stepped component (4 states), example Age:
"age": {
"values": ["baby", "child", "young", "old"],
"steps": [0.0, 0.1875, 0.5, 1.0],
"pattern": "macrodetails/Age"
}
How it works:
- Four age ranges with different step sizes
- 0.0 to 0.1875 → baby to child transition
- 0.1875 to 0.5 → child to young transition
- 0.5 to 1.0 → young to old transition
- Non-uniform steps allow different aging speeds
Barycentric component (sum-based), example: Phenotype:
"phenotype": {
"values": ["african", "asian", "caucasian"],
"pattern": "macrodetails/",
"sum": ["African", "Asian", "Caucasian"]
}
How it works:
- Reads three barycentric slider values that sum to 1.0
- Each corner can be 0% to 100%
- Pattern + sum entry = full target path (e.g. macrodetails/African)
- All three values are read simultaneously
- Example: 60% african, 20% asian, 20% caucasian
Standard Components
Most base meshes use these standard components:
| Component | Values | Type | Purpose |
| gender | female, male | Stepped(2) | Gender morphing |
| age | baby, child, young, old | Stepped (4) | Age progression |
| muscle | minmuscle, averagemuscle, maxmuscle | Stepped (3) | Muscle definition |
| weight | minweight, averageweight, maxweight | Stepped (3) | Body mass |
| phenotype | african, asian, caucasian | Barycentric | Ethnic features |
| proportion | uncommon, none, ideal | Stepped (3) | Beauty proportions |
| height | minheight, none, maxheight | Stepped (3) | Height adjustment |
| cupsize | mincup, averagecup, maxcup | Stepped (3) | Breast size (female) |
| firmness | minfirmness, averagefirmness, maxfirmness | Stepped (3) | Breast firmness |
Macrodef Section
The
macrodef array defines macro target definitions - collections of targets that represent every possible combination of component states.
Macrodef Structure:
{
"name": "descriptive-macro-name",
"comp": ["component1", "component2", "component3"],
"folder": "target-subfolder",
"targets": [
{"name": "component1-component2-component3", "t": "actual-file-name"}
]
}
Property Reference
| name string, required |
Descriptive name for this macro definition, it is used for logging and debugging and should
indicate which components are involved.
|
Example:
"phenotype-gender-age"
"gender-age-muscle-weight"
|
| comp array of strings, required |
List of component names used in this macro, which must reference components defined in the components section.
The order matters - determines how target names are constructed.
|
Example:
["gender", "age", "muscle"]
|
| folder string, required |
Subdirectory containing the target files for this macro, it is relative to the target directory.
|
Examples:
"macro_age"
"macro_muscle"
"macro_breast"
|
| targets array of objects, required |
Complete list of all possible target combinations, each entry maps a calculated name to an actual target file,
it's purpose is to re-use identical target files. Each entry consists of a 'name' based on component value combinations and 't',
the actual target filename without .target extension. If 't' is null, it is average, so no target is needed.
|
Example:
{"name": "female-child-averagemuscle-averageweight-uncommon",
"t": "female-child2old-allmuscle-allweight-uncommon"}
|
How Macro Calculation Works
When a macro slider changes (like Gender, Age, or Weight), the system:
- Reads current values of all relevant sliders
- Calculates component weights based on slider positions
- Generates target combinations by crossing all component values
- Applies interpolation weights for smooth transitions
- Maps calculated names to actual target files
- Applies targets with appropriate factors to the mesh
Step-by-Step Process
Step 1: Identify Influenced Macros
Each macro target in modelling.json has a macro_influence field:
"Gender": {
"macro": "macrodetails/Gender",
"macro_influence": [0, 1, 2, 3, 4]
}
This indicates which macrodef entries (by index) are affected by this slider.
Step 2: Read Component Values
For each macro definition, read the current slider values for all components:
# For macrodef with comp: ["gender", "age", "muscle"]
gender_value = slider["macrodetails/Gender"].value / 100 # 0.0 to 1.0
age_value = slider["macrodetails/Age"].value / 100
muscle_value = slider["macrodetails-universal/Muscle"].value / 100
Step 3: Calculate Component Weights
For stepped components, interpolate between adjacent values:
# Gender with steps [0.0, 1.0] and values ["female", "male"]
if gender_value <= 0.5:
weights = {"female": 1.0 - (gender_value / 0.5), "male": gender_value / 0.5}
else:
weights = {"female": 0.0, "male": 1.0}
For barycentric components, read the three corner values:
# Phenotype with sum ["African", "Asian", "Caucasian"]
weights = {
"african": slider["macrodetails/African"].barycentric[0].value,
"asian": slider["macrodetails/Asian"].barycentric[1].value,
"caucasian": slider["macrodetails/Caucasian"].barycentric[2].value
}
Step 4: Generate Target Combinations
Cross-multiply all component weights to generate every combination:
# Example: gender × age × muscle
combinations = []
for gender_name, gender_weight in gender_weights.items():
for age_name, age_weight in age_weights.items():
for muscle_name, muscle_weight in muscle_weights.items():
combined_name = f"{gender_name}-{age_name}-{muscle_name}"
combined_weight = gender_weight * age_weight * muscle_weight
if combined_weight > 0.01: # Skip negligible weights
combinations.append((combined_name, combined_weight))
Step 5: Map to Actual Targets
Look up each calculated name in the targetlink dictionary:
for calculated_name, weight in combinations:
if calculated_name in targetlink:
actual_target = targetlink[calculated_name]
if actual_target is not None:
apply_target(actual_target, weight)
Step 6: Apply Targets
Add all selected targets to the mesh with their calculated weights:
mesh.prepareMacroBuffer()
for target_path, weight in target_applications:
target_data = load_target(target_path)
mesh.addTargetToMacroBuffer(weight, target_data)
mesh.addMacroBuffer() # Apply all at once
Example Calculation
Scenario: Slider values are:
- Gender: 0.75 (75% male, 25% female)
- Age: 0.5 (100% young)
- Muscle: 0.25 (50% min, 50% average)
Component Weights:
gender: {"female": 0.25, "male": 0.75}
age: {"young": 1.0}
muscle: {"minmuscle": 0.5, "averagemuscle": 0.5}
Generated Combinations:
female-young-minmuscle: 0.25 × 1.0 × 0.5 = 0.125
female-young-averagemuscle: 0.25 × 1.0 × 0.5 = 0.125
male-young-minmuscle: 0.75 × 1.0 × 0.5 = 0.375
male-young-averagemuscle: 0.75 × 1.0 × 0.5 = 0.375
Result: 4 targets applied with weights 12.5%, 12.5%, 37.5%, 37.5%
Example Macro Definitions
Simple 2-Component Macro, Gender × Age (24 targets)
{
"name": "phenotype-gender-age",
"comp": ["phenotype", "gender", "age"],
"folder": "macro_age",
"targets": [
{"name": "african-female-baby", "t": "african-female-baby"},
{"name": "african-female-child", "t": "african-female-child"},
{"name": "african-female-young", "t": "african-female-young"},
{"name": "african-female-old", "t": "african-female-old"},
{"name": "african-male-baby", "t": "african-male-baby"},
{"name": "african-male-child", "t": "african-male-child"},
{"name": "african-male-young", "t": "african-male-young"},
{"name": "african-male-old", "t": "african-male-old"},
...
]
Combinations: 3 phenotypes × 2 genders × 4 ages = 24 targets
Complex 4-Component Macro, Gender × Age × Muscle × Weight (72 targets per gender)
{
"name": "gender-age-muscle-weight",
"comp": ["gender", "age", "muscle", "weight"],
"folder": "macro_muscle",
"targets": [
{"name": "female-baby-averagemuscle-averageweight", "t": null},
{"name": "female-baby-averagemuscle-maxweight", "t": "female-baby-averagemuscle-maxweight"},
{"name": "female-baby-averagemuscle-minweight", "t": "female-baby-averagemuscle-minweight"},
{"name": "female-baby-maxmuscle-averageweight", "t": "female-baby-maxmuscle-averageweight"},
...
]
}
Combinations:2 genders × 4 ages × 3 muscles × 3 weights = 72 targets
Note: Some targets may be `null` (base state) or reused across combinations.
Highly Complex 6-Component Macro, Gender × Age × Muscle × Weight × Cup × Firmness (Female only, 324 targets)
{
"name": "gender-age-muscle-weight-cup-firmness",
"comp": ["gender", "age", "muscle", "weight", "cupsize", "firmness"],
"folder": "macro_breast",
"targets": [
{"name": "female-young-averagemuscle-averageweight-averagecup-averagefirmness", "t": null},
{"name": "female-young-averagemuscle-averageweight-averagecup-maxfirmness", "t": "young-averagemuscle-averageweight-averagecup-maxfirmness"},
{"name": "female-young-averagemuscle-averageweight-averagecup-minfirmness", "t": "young-averagemuscle-averageweight-averagecup-minfirmness"},
...
]
}
Combinations: 1 gender × 4 ages × 3 muscles × 3 weights × 3 cups × 3 firmnesses = 324 targets (female only)
Target File Naming Strategy
Systematic Naming: Target files follow the exact naming pattern of component values:
{folder}/{value1}-{value2}-{value3}-{...}.target
Example:
macro_muscle/female-young-maxmuscle-minweight.target
Reuse and Aliasing: Some combinations can share the same target file to reduce duplication:
{"name": "female-baby-minmuscle-averageweight", "t": "universal-baby-minmuscle-averageweight"},
{"name": "male-baby-minmuscle-averageweight", "t": "universal-baby-minmuscle-averageweight"}
Both map to the same file because baby muscle/weight isn't gender-specific.
Abbreviated Naming: Target filenames may use abbreviated forms to reduce redundancy:
{"name": "female-young-averagemuscle-averageweight-averagecup-maxfirmness",
"t": "young-averagemuscle-averageweight-averagecup-maxfirmness"}
Gender prefix omitted when folder is female-specific.
Null Targets: Base state combinations may have null targets (no morph needed):
{"name": "female-young-averagemuscle-averageweight", "t": null}
This represents the default state - no morphing applied.
Integration with other files
| Base Configuration |
Start values of targets may be overridden by modifier-presets in base.json
|
See: base.json for base configuration
|