Character Cards
Load and use V3 character cards for structured avatar personalities.
Character cards define an avatar's personality, backstory, scenario, and behavior examples in a structured format. AvatarLayer supports the V3 character card specification and can load cards from PNG images or JSON.
Using a character card
Set characterCard in the session config. When present, it takes precedence over systemPrompt:
import {
AvatarSession,
OpenAIAdapter,
ElevenLabsAdapter,
VRMLocalRenderer,
parseCharacterCard,
} from "avatarlayer";
const response = await fetch("/characters/assistant.json");
const card = parseCharacterCard(await response.text());
const session = new AvatarSession({
llm: new OpenAIAdapter({ apiKey: "sk-..." }),
tts: new ElevenLabsAdapter({ apiKey: "..." }),
renderer: new VRMLocalRenderer({ modelUrl: "/models/avatar.vrm" }),
characterCard: card,
});Parsing character cards
From JSON
import { parseCharacterCard } from "avatarlayer";
const json = await fetch("/characters/my-char.json").then(r => r.text());
const card = parseCharacterCard(json);From PNG (embedded metadata)
Character cards can be embedded in PNG image metadata (following the V3 spec). Pass the raw bytes:
import { parseCharacterCard } from "avatarlayer";
const buffer = await fetch("/characters/my-char.png").then(r => r.arrayBuffer());
const card = parseCharacterCard(new Uint8Array(buffer));Updating at runtime
session.setCharacterCard(newCard);Building prompt messages
Under the hood, AvatarSession uses buildCardMessages to construct the LLM messages from a card. You can also use this directly:
import { buildCardMessages } from "avatarlayer";
const messages = buildCardMessages(card, conversationHistory, {
charName: "Luna", // defaults to card.name
userName: "User", // default: "User"
emotionSuffix: true, // append emotion marker instructions
});This produces a message array with:
- A system message built from the card's
description,personality,scenario, andsystem_promptfields - Matched lorebook entries injected as context
- Message examples from the card
- The provided conversation history
Lorebook matching
Lorebook entries are automatically matched against the conversation history:
import { matchLorebookEntries } from "avatarlayer";
const matched = matchLorebookEntries(card.data.character_book, messages);Each entry's keys are tested against message content. Matched entries have their content injected into the LLM context.
CharacterCard type
interface CharacterCard {
spec: "chara_card_v3";
spec_version: "3.0";
data: CharacterCardV3Data;
}
interface CharacterCardV3Data {
name: string;
description: string;
personality: string;
scenario: string;
first_mes: string;
mes_example: string;
system_prompt: string;
creator_notes: string;
tags: string[];
creator: string;
character_version: string;
character_book?: CharacterBook;
assets?: CharacterCardAsset[];
// ...additional V3 fields
}