Open app
Moonborn — Developers

Soul, Self, Mask, Surface

Why generated characters feel flat — and how Moonborn's four-layer model produces personas that hold up under conversational pressure.

The flatness problem

Most LLM character pipelines write the Surface — name, age, occupation — and stop. The result is a costume, not a character. Drop it into a long conversation and the seams show: the voice flattens, the values shift, the "person" turns into a polite assistant in a hat.

Moonborn flips the order. Each persona is generated inside-out: psychology first, paint last. Four layers, each constraining the next.

Soul · ember

Core desire, fear, wound, growth arc. The thing the character wants and what stops them.

{
 "desire": "to build something the world can't ignore",
 "fear": "being seen as ordinary",
 "wound": "a parent who measured love in achievement",
 "growth_arc": "from approval-seeking to self-trusting"
}

Without Soul, every downstream layer is decoration. A persona with no wound can't sound real about loss; a persona with no desire can't have stakes in a conversation.

Self · moss

Big Five traits, archetype, values, attachment style. Psychological coherence between situations — the same person across contexts.

{
 "bigFive": { "openness": 0.78, "conscientiousness": 0.71, "extraversion": 0.55, "agreeableness": 0.42, "neuroticism": 0.68 },
 "archetype": "the rebel",
 "values": ["competence", "autonomy", "honesty"],
 "attachment": "anxious-preoccupied"
}

Self is the bridge between Soul (what they want) and Mask (how they speak). It governs how the character behaves under pressure.

Mask · gold

Voice, tone, signature phrases, social role. This is what users actually hear. Mask is captured into a voice fingerprint — a numeric embedding that the chat runtime checks every reply against.

{
 "voice": {
 "register": "intelligent, restless, occasionally sharp",
 "rhythm": "short clauses; long pauses imagined",
 "vocabulary": "tech-fluent, with bookish detours"
 },
 "signaturePhrases": ["look — ", "the thing is", "fine, let me think out loud"],
 "socialRole": "ambitious peer"
}

Mask is also the layer most exposed to drift. Long sessions, off-topic steering, and provider model swaps all chip away at it.

Surface · sky

Name, age, location, occupation, appearance. The grounding — concrete details that make a persona memorable and prevent the character from sliding into a stereotype.

{
 "name": { "display": "Mert Aksoy", "given": "Mert", "family": "Aksoy" },
 "age": 34,
 "location": "Beyoğlu, Istanbul",
 "occupation": "Founder, devtools startup",
 "appearance": "linen shirts; never quite enough sleep"
}

Surface is generated last, on purpose. If you write Surface first the model anchors on the cliché ("Istanbul founder = …") and back-fills the other layers to match. Generating inside-out forces specificity.

Generation order is part of the design

Soul → Self + Mask → Surface → Audit → Voice fingerprint

Soul drafts first (Claude Opus). Self and Mask enrich in parallel (Claude Sonnet). Surface grounds the persona with concrete details. An LLM-as-judge audits coherence across all four layers; a voice fingerprint is captured for runtime drift detection.

Every step is configurable — model, temperature, provider fallback — but the order is not. Inverting it breaks the constraint chain.

Why this matters at scale

  • Coherence under drift. Drift detection compares each chat reply to the Mask fingerprint. A persona without a real Mask can't be defended.
  • Brand-safe voice across thousands of sessions. Soul + Mask + voice fingerprint = the same character whether session #1 or #10,000.
  • Fork and version each layer independently. Want a "more formal" variant? Fork, refine the Mask, leave Soul intact. Lineage tracks every fork.

Continue with Voice fingerprint for the runtime side, or jump to a vertical in Use cases.