Traits
traitsTraits are the character creation choices - race, class, background, and any other categories you define. Each trait bundles the starting capabilities the chosen character receives.
"Character traits"
- Editor location
- Files → Mechanics → Traits
- Editor type
- Edit in Studio + View JSON
- API endpoint
/sections/traits.json/sections/traits.md- Size limits
traits.*.descriptionandtraits.*.traitNarrativeEffectscombined — 6,000traits(entire section) — 1,750,000traits.*.requirements(array length) — 10 entries
Schema
{
"traits": {
"<key>": {
"name": "string",
"description": "string",
"traitNarrativeEffects": "string",
"skills": [
{
"skill": "string",
"modifier": "number"
}
],
"attributes": [
{
"attribute": "string",
"modifier": "number"
}
],
"resources": [
{
"resource": "string",
"modifier": "number"
}
],
"startingItems": [
{
"item": "string",
"quantity": "number"
}
],
"abilities": [
"string"
],
"unlockedBy": [
"string"
],
"excludedBy": [
"string"
],
"requirements": [
{
"type": "resource | attribute | skill | trait",
"variable": "string",
"amount": "number"
}
],
"vulnerabilities": [
"string"
],
"resistances": [
"string"
],
"immunities": [
"string"
]
}
}
}Example
{
"traits": {
"Human": {
"name": "Human",
"description": "Humans are adaptable and ambitious. Quick to learn and equally quick to scheme. They dominate the central territories politically and commercially.",
"skills": [
{ "skill": "persuasion", "modifier": 10 },
{ "skill": "history", "modifier": 10 }
],
"attributes": [
{ "attribute": "charisma", "modifier": 1 },
{ "attribute": "intelligence", "modifier": 1 }
],
"resources": [
{ "resource": "stamina", "modifier": 10 }
],
"traitNarrativeEffects": "You are Human. Humans are adaptable and opportunistic — no environment is entirely foreign, and no skill tree is completely closed off. When you attempt something outside your established capabilities, apply a subtle edge of competence that reflects a lifetime of picking things up from context. NPCs from other species may underestimate you initially; humans read as ordinary, which they use. In social situations where your background would be unknown, default to being underestimated rather than recognised.",
"startingItems": [
{ "item": "leather boots", "quantity": 1 }
],
"abilities": ["Determined Stand"],
"unlockedBy": [],
"requirements": [],
"excludedBy": ["Elf", "Dwarf", "Halfling"]
}
}
}Fields
name
Display name of the trait, and must be byte-identical to the outer map key — the engine matches entries by key, so the key and name have to be exactly equal (same case, same spaces). A mismatch is a validation error. Build the map as { trait["name"]: trait }.
resources
Adjusts resource pool maximums or starting values at character creation.
"resources": [
{ "resource": "health", "modifier": 20 },
{ "resource": "mana", "modifier": -10 }
]Use for races or classes that should have significantly more or less of a given resource pool. Positive values increase the pool max; negative values reduce it.
description
Player-facing description shown during trait selection.
traitNarrativeEffects
The narrator's primary reference for how the trait behaves in play.
attributes
Additive attribute modifiers as { attribute, modifier } pairs; positive or negative, stacking across traits. Attribute modifiers apply before resources initialize, so an attribute boost also feeds attributeStatModifiers.
skills
Additive skill level modifiers ({ skill, modifier }).
startingItems
Items granted when the trait applies, as { item, quantity } with keys from itemTypes. A negative quantity removes that many of the item instead (useful for backgrounds that take away default gear); 0 is a no-op.
abilities
Ability names unlocked by the trait; deduplicated across sources.
vulnerabilities
Damage types a character with this trait is vulnerable to, taking 1.5x damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.
resistances
Damage types a character with this trait is resistant to, taking 0.5x damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.
immunities
Damage types a character with this trait is immune to, taking no damage from each. Every value must match an entry in combatSettings.damageTypes; a value that does not is reported as a warning and has no mechanical effect.
unlockedBy
Trait prerequisites (OR logic). Character-creation starting selection ignores them - every trait its category exposes stays selectable. They take effect for level-up trait picks: a pool trait is offered only once the character holds at least one listed trait. The engine can always set traits directly, so premades and triggers bypass it either way.
excludedBy
Traits this one conflicts with. Character-creation starting selection ignores this - the pair is not made mutually exclusive there. It takes effect for level-up trait picks: a pool trait is dropped from the offer once the character holds any listed trait. The engine can always set traits directly, so premades and triggers bypass it either way.
requirements
Optional array of requirement entries. They gate whether the trait is offered as a level-up trait pick (see Level-up trait picks); character-creation starting selection ignores them. Each entry's type is one of resource, attribute, skill, trait, or characterLevel.
Required arrays
These four fields must be present on every trait, even when empty:
startingItems-Array<{item, quantity}>- required even if[]. A negativequantityremoves that many of the item instead of granting it (useful for backgrounds that strip default gear);0is a no-opabilities-Array<string>- required even if[]unlockedBy-Array<string>- required even if[]excludedBy-Array<string>- required even if[]
Assigning a trait to a category
A trait is placed in a category by listing its name in that category's traits array (see traitCategories). The trait carries no category field of its own — the link runs one way, from the category down to its members. To move a trait between categories, edit the traits arrays, not the trait.
Attribute modifiers
The attribute string must exactly match one of the names defined in attributeSettings.attributeNames. Using a name that does not appear there causes a validation warning. Always cross-check against your world's attribute list before authoring trait modifiers.
Skill modifiers
Start at +10 for frequently used skills, +20 for rarely used ones. Negative modifiers are valid for tradeoff builds.
Note (trait
skillsarray): Granting a skill via a trait is a mechanical unlock. If the player did not already have the skill, the engine adds it to their sheet at level 0 and then applies the modifier, so the trait's modifier is the level they end up with. If the character already had the skill, only the modifier applies.
Narrative effects
traitNarrativeEffects is the AI narrator's reference for the trait during play. When a character has a trait, the narrator reads traitNarrativeEffects as the active instruction for how that trait affects the world — how NPCs treat the character, what the character can and cannot do, what behavioral constraints apply. A King trait with traitNarrativeEffects: "The player is a king. NPCs recognize and treat them as royalty." makes the character actually function as a king in play. A Turned trait with a detailed behavioral specification produces a character who cannot speak, cannot reason, and draws NPC fear reactions consistently across every scene. By default traitNarrativeEffects carries the same content as description (see below); write it as the narrator's working guidance for the trait.
description vs traitNarrativeEffects
description is what the trait is — shown to the player in the selection UI at character creation, so write it as a readable, informative paragraph. traitNarrativeEffects is the narrator's reference for the trait during play.
By default, write description and traitNarrativeEffects as the same full-paragraph content — the player-facing explanation usually doubles as the narrator's portrayal guidance. Let them diverge only when:
- the
traitNarrativeEffectsneeds to carry hidden aspects the player should not see (true motives, secret consequences, GM-only behavioral constraints), or - the
descriptionis a shorter summary and thetraitNarrativeEffectsis the fuller, elaborated version the narrator works from.
For species traits (races), the traitNarrativeEffects tends to run longer than the player-facing description regardless, because the narrator needs more behavioral context to portray the species consistently across every scene.
Damage profile (resistances, vulnerabilities, immunities)
Optional. A trait can grant the character permanent damage modifiers through three string arrays of damage types:
| Field | Multiplier | Effect |
|---|---|---|
vulnerabilities | 1.5x | +50% damage taken |
resistances | 0.5x | -50% damage taken |
immunities | 0x | no damage taken |
"resistances": ["fire", "poisoning"],
"vulnerabilities": ["water"],
"immunities": ["dark"]These three are optional - unlike the required arrays above, omit them when a trait has no damage profile. The modifiers are permanent for as long as the character holds the trait. They use the same damage system as npcType damage profiles: values must match strings in combatSettings.damageTypes verbatim, and any value that is not a defined damage type has no mechanical effect.
Trait design philosophy
Think of traits as a bucket that can contain any number of skills, attributes, resources, abilities, startingItems, and narrative effects. Races and classes use the exact same structure. The world creator decides how much mechanical weight each carries.
Level-up trait picks
When progressionSettings.levelUpTraitPool lists traits, a character is offered a trait pick every traitPickEveryLevels levels. The offered list is that pool filtered to traits the character does not already hold, whose unlockedBy / excludedBy conditions pass, and whose requirements are met. A chosen trait applies exactly like a starting trait: every skill, attribute, resource, ability, and item modifier fires. A pool trait does not need a traitCategories entry; it can be pool-only (never a character-creation option) or both.
unlockedBy(OR logic): the trait is offered only once the character holds at least one listed trait.excludedBy: the trait is dropped from the offer once the character holds any listed trait.requirements: each entry (resource/attribute/skill/trait/characterLevel) must be satisfied.
Character creation ignores all three. Starting-trait selection offers every trait its category exposes regardless of unlockedBy, excludedBy, or requirements: that gating was designed for the creator but is not implemented there. The only hard character-creation constraint is the category selection limit (a required category with max 1 enforces pick-exactly-one). The engine can always set traits directly, so premades and triggers bypass all of this either way.
Note: Do not use
excludedByon Class traits to prevent multi-class picks at creation - the category's single-selection limit already handles that.
Editor caveats
Warning: The following trait fields have no graphical controls in the editor (only editable via the JSON tab):
startingItems,abilities,unlockedBy,excludedBy. All four are required - omitting any causes decoder errors even when the array is empty. ThetraitCategories.traitsarray (listing trait names per category) is also required, and is what places each trait in its category.
Trait removal at runtime
Via player-traits trigger effect:
- Attribute modifiers subtracted
- Skill modifiers subtracted
- Resource modifiers subtracted (current resource value may drop if it sat above the new max)
- Abilities removed if no other source still grants them
Dynamic trait changes via triggers
For the canonical worked example of swapping one race trait for another mid-game (with the transformation delivered as a present-tense scene interrupt), see the Race Evolution worked example on the Scripting Patterns page. The branching-paths variant handles player-choice trait progression.
{ "type": "player-traits", "operator": "add", "value": "Cursed" }
{ "type": "player-traits", "operator": "remove", "value": "Cursed" }The same modifier pipeline runs as during character creation, so stat changes flow through automatically.
Authoring tips
3-tier trait chain (Race → Origin → Class)
- Origin traits:
"unlockedBy": ["Human"](or relevant race) - Class traits:
"unlockedBy": ["City Guard Background"](or relevant origin) - Competing origins:
"excludedBy": ["Other Origin 1", "Other Origin 2"]
Ability-granting and class design patterns
abilities is the primary ability-granting mechanism — not ability requirements. Abilities listed in a trait's abilities array are granted directly to any character who takes that trait, bypassing the ability's requirements field entirely. This is the correct and reliable way to give characters class-specific or character-specific starting abilities. The requirements field on an ability controls what can be learned during play (and only skill type requirements are enforced). Do not rely on ability requirements to prevent a trait from granting an ability — if the trait's abilities array contains it, the character gets it regardless.
Character-exclusive abilities
Create a trait specific to that character (e.g. "Merlin's Gifts") and list the exclusive abilities in its abilities array. Do NOT place those abilities on a generic class trait (e.g. "Archmage") that other characters might also take — any character with the generic trait receives all abilities in that trait's abilities array. The abilities array bypasses requirements entirely, so listing an ability on a generic class trait grants it to all characters with that trait. The exclusive trait must also be absent from all traitCategories so custom characters cannot select it in the character creator.
Two tiers of ability within a character-specific trait
- Abilities with no learnable skill path — truly exclusive. If no skill threshold in the world would let a player naturally reach an ability, the only way to have it is via the trait
abilitiesarray. Use this for abilities that define the character's uniqueness and must never be learnable by any other character during play. - Skill-threshold requirement — learnable by any character. An ability with
{"type": "skill", ...}requirements will appear in any character's learnable pool once they meet the threshold, regardless of what trait it is also listed on. Listing it on a trait grants it at character creation for premade characters, but it remains universally learnable during play. Do not use skill-threshold requirements to try to restrict an ability to a single character.
When designing a character-specific trait: use abilities with no reachable skill path for defining uniqueness. Use skill-threshold requirements for abilities strongly associated with that character archetype but legitimate for any sufficiently trained character.
Class-based ability isolation
The trait skills array is the real gate for which ability trees a class can access. If a magic class trait grants no martial arts skill bonus, mage characters have 0 in that skill and cannot reach any martial arts threshold regardless of what abilities exist in the world. Keep skill bonus families strictly separated by class type — cross-contamination in the skills array allows characters to unlock abilities from the wrong tree through ordinary skill progression.
Tiered power via class variants
To gate high-tier content to premium characters only, give the relevant high-tier skill bonuses exclusively to premium class traits. Base-tier class traits max out at lower skill bonuses, so their characters cannot reach higher ability thresholds. Premium-tier class traits add the higher skill bonuses, unlocking those abilities for premium characters only. The skill threshold system enforces the gate automatically. Race traits may legitimately add cross-class skill bonuses; class traits should stay within their ability domain.
Multi-axis character creation
A well-defined character has 3-5 distinct trait axes the player chooses from at creation. Each axis is its own traitCategory and contributes different things to the character. The D&D-style four-axis layout is well-tested and the AI handles it natively out of the box:
- Race - physical/biological traits
- Background - history and starting skills
- Class - combat/profession role
- Alignment - moral compass (D&D 9-alignment system or equivalent)
It is not the only working layout. A modern scenario might use Profession + Origin + Personality; a sci-fi setting might use Species + Faction + Specialization + Era; a slice-of-life world might use just Background + Personality + Defining Relationship; a horror world might collapse to Era + Survival Background + Mental State. Pick the axes that produce meaningful choices in your genre, then design traits within each. The schema is genre-agnostic — the structure is whatever your world needs.
Trait design rules
Illustrated D&D-style; the rules generalize to other axis structures.
- Give each trait a
traitNarrativeEffectsthat instructs the narrator on the trait's signature features and behavioral constraints (e.g., "This character has Second Wind: once per short rest they can recover HP as a bonus action. The narrator should apply this and similar class features consistently."). Thedescriptionfield is what players read in the selection UI;traitNarrativeEffectsis what the AI narrator reads during play. The same applies to non-class traits — a Background, Profession, Faction, etc. can carry atraitNarrativeEffectsdescribing how that origin colors the character. - Include
startingItemsthat fit the trait's role and any weight/equipment restrictions you want signalled at character creation. - Do not use
excludedByto prevent same-category multi-selection (e.g. two classes, two factions) —excludedByhas no enforcement in the UI. The category's single-selection limit already ensures one pick per category. - Skill bonuses come in two patterns depending on design intent: focus traits (+15 to one defining skill, no secondary) for archetypes or specializations where the trait defines a single capability; class traits (+5 flat across 2-4 relevant skills) for broadly-skilled characters where the trait represents a role. Both are valid. The +15 single-skill pattern rewards focused investment; the +5 multi-skill pattern distributes the bonus across a wider capability surface. Choose based on how tight you want the class identity to be.
- Attribute bonuses for race traits: +1 to +3 per attribute, typically 2-3 positive stats. Including negative modifiers on race traits (offset by higher positive values) is a valid tradeoff approach. Background/origin traits typically grant smaller attribute bonuses (+1/+1) than racial traits.
Equipment restrictions via aiInstructions
The trait system enforces starting items but not ongoing use restrictions. If your world wants ongoing restrictions (e.g. "mages can only use light weapons", "civilians can't carry firearms", "common-tier characters can't wield Tier 3 equipment", "non-pilots can't operate mechs"), add an Equipment Restrictions section to aiInstructions.generateStory defining who can use what.
D&D-style example:
Mages (Wizard, Sorcerer, Warlock): light implements only. Cannot wield swords, axes, or medium/heavy armour.
Fighters: all weapon types. Cannot cast spells without a multiclass.Modern example:
Civilians: cannot equip firearms or military gear.
Trained Operators: may equip any class of firearm; subject to ammo availability.The mechanism is genre-agnostic — the restrictions are however your world frames "appropriate gear".
Hiding trigger-only traits from the character creator
If you have traits that are assigned exclusively by triggers (status effects, hidden states, condition stages, etc.) and should not be player-selectable, the correct pattern is:
- Do not create a traitCategory entry for them. Any entry in
traitCategories- even with an emptytraitsarray - renders a tab in the character creator. - Do not put a
categoryfield on those traits. Without a traitCategory entry to reference, the validator will error on anycategoryvalue pointing to a missing category. Either omitcategoryentirely or remove it.
The traits still exist in the traits dict and are fully functional - triggers can assign them by key name at any point during play. They simply have no character-creator presence.