Advanced Configuration
Fine-tune per-project behavior with config.json — model overrides, file mappings, and more.
- Overview of config.json
- Model Overrides
- File Mappings
- Command Palette
- Voice and Push-to-Talk Settings
- Input Corrector
- Visualize Settings
- Per-Project Agents
- Settings Hierarchy
- The .gitignore Tab
Most of what you need to configure in Character Studio lives in two places: the global Settings dialog (which applies across all your projects) and the per-project config.json file (which overrides the global settings for one character). This chapter covers config.json in depth. If the Settings dialog is where you set your API key and default model, config.json is where you fine-tune each character to perform exactly the way you want — different model, different sampling parameters, custom file layout, agent selection, voice behavior, and more.
Overview of config.json
Every project has a config.json file, visible in the Config tab in the left pane. It starts empty (an empty JSON object, {}) when you create a project, meaning all settings fall through to global defaults. As you add fields, they override the corresponding global settings for this project only. No other project is affected.
The Config tab is powered by Monaco Editor with full IntelliSense. Press Ctrl+Space anywhere inside the JSON object to see all available fields as an autocomplete list, each with a short description. Hover over any key to see its documentation inline. The editor validates your JSON continuously against the config schema, and unknown or mistyped fields are flagged with red underlines before you even try to save.
When you save a config (via the Save Changes button or Ctrl+S), Studio validates the file. If there's a JSON syntax error or a value that violates the schema, an error modal explains the problem and the Config tab label turns red. The invalid config is not applied; you need to fix the issue before saving. This prevents a malformed config from silently corrupting your character's behavior.
If a project's config.json contains a syntax error when you open the project, a modal displays the exact parse error and automatically opens the Config tab so you can correct it before doing anything else. You won't be able to start a conversation until the config is valid.
Model Overrides
The most common reason to edit config.json is to pin a specific character to a particular model. Your global default might be a versatile mid-tier model that handles most characters well, but a particular character might perform measurably better on a different model — one with stronger creative fiction output, or a larger context window, or a particular personality in its base behavior that happens to fit your character's voice. Rather than changing your global model and affecting all your other projects, you set it in this character's config.
"model" sets the primary LLM used for all conversations with this character. "workerModel" sets the model used for background tasks: the Playtest user-player, the Input Corrector, the Collapse to Here summarizer, the Visualize image prompt generator, and the Description generator. These tasks don't need the quality of your primary model — a smaller, faster, cheaper model is usually the right choice here.
The "temperature" field controls sampling temperature, from 0 (deterministic, always picks the highest-probability next token) to 2 (highly creative and unpredictable). The right temperature depends on your character: a technical assistant benefits from lower temperature and more consistent output; a chaotic trickster character or a poetic narrator benefits from higher temperature and more varied phrasing. Most characters land between 0.7 and 1.1. The "topP" field (nucleus sampling) is an alternative or complement to temperature — some models support both, some only support one. Studio detects model capabilities automatically, so you can set both and the unsupported one will be gracefully ignored.
{
"model": "llama-3.3-70b",
"workerModel": "llama-3.1-8b",
"temperature": 0.9,
"topP": 0.95
}
File Mappings
By default, each tab in Character Studio reads a fixed filename: the Instructions tab reads instructions.txt, the System Prompt tab reads system-prompt.txt, the Description tab reads description.txt, and so on. The fileMappings field lets you override these defaults, pointing any tab at a different filename within the project folder.
This is useful in a few common situations. You might have a family of related characters who share a common instruction base — a world's worth of lore and shared mechanics — and you want them all to read from the same shared_base.txt rather than maintaining copies. You might have versioned instruction files (aria_v2.txt, aria_v3.txt) and want to switch between them without editing the file itself. Or you might simply prefer a different file naming convention than the Studio default.
{
"fileMappings": {
"instructions": "aria_v3.txt",
"systemPrompt": "base_prompt.txt",
"context": "lore/"
}
}
The "context" mapping is particularly powerful: it changes the entire folder from which context files are read. If you have multiple characters who all draw from a shared lore/ directory of world-building files, you can point all of them at that folder rather than copying files between projects. Edits to the shared lore files are immediately reflected in every character that references them.
File mapping paths must stay within the project folder — ../ traversal up the directory tree is not possible. Browser sandboxing restricts file access to the directory handle the user granted when opening the project, and there is no mechanism to escape that boundary. If you point a mapping at a file that doesn't exist, the tab will show an error when you try to open it.
Command Palette
The Commands dropdown in the chat toolbar gives you quick access to pre-written message templates — things the user might say that you want to be able to trigger in one click during testing, or shortcuts for common actions your character is designed to respond to. The "commandPalette" field in config.json controls what appears in that list.
The command list is auto-populated when you click the ↺ Refresh Command List button in the chat toolbar, which sends your system prompt to the worker model and asks it to extract a list of likely user commands or actions relevant to your character. This gives you a sensible starting list based on your character's design. But automatic extraction is imperfect — it doesn't know your testing workflow, your most-needed shortcuts, or the specific phrasings you prefer. That's why the list is stored in config.json where you can edit it directly.
Each command in the palette is a string. When you click a command in the dropdown, that string is inserted into the chat input, where you can send it as-is or edit it before sending. You can add any strings you like, reorder them, and remove the ones you don't use. A well-curated command palette makes Playtest setup faster and makes it easy to quickly test specific scenario branches without retyping the same setup messages repeatedly.
{
"commandPalette": [
"What can you tell me about the artifact?",
"I want to attempt to pick the lock.",
"I attack the guard.",
"What do I know about this area?",
"Check my stats."
]
}
Voice and Push-to-Talk Settings
Voice features — Push-to-Talk recording, speech-to-text model, microphone selection, and key bindings — have both global settings and per-project overrides. The per-project overrides in config.json exist because different characters may benefit from different voice configurations. A character designed for dictation-heavy creative writing sessions might have PTT enabled on a convenient key with a high-quality STT model. Another character designed for quick casual conversations might have voice disabled entirely to avoid accidental activations.
| Config key | Description |
|---|---|
voice.enabled |
Enable or disable Push-to-Talk for this project, regardless of the global setting. |
voice.pttKey |
The key used to activate recording. Overrides the global PTT key binding. |
voice.pttModifiers |
Array of modifier keys held with the PTT key. Accepted values: "ctrl", "shift", "alt". |
voice.microphoneId |
Device ID for the microphone to use. Useful when you have multiple audio input devices and different characters benefit from different ones. |
voice.sttModel |
Speech-to-text model ID. Override the global STT model for this project. |
Input Corrector
The Input Corrector (covered in Chapter 4) is a global feature — when enabled in Settings, it's available in every project. But there are characters for whom automatic correction is counterproductive. A creative writing assistant where you deliberately write in a fragmented, stylized way. A character designed for quick shorthand inputs where you intentionally abbreviate. A non-English character where your inputs contain intentional dialectal spelling the corrector will flag as errors.
Set "inputCorrectorEnabled": false in that project's config.json to disable the corrector for this character only. The global setting and all other projects are unaffected. You can also set it to true to force it on for a project even if you've turned it off globally, which is useful if you want correction for formal writing characters but not casual ones.
Visualize Settings
The Visualize feature (the camera button in the chat toolbar) generates an image prompt from the current conversation, then queues a generation in the Image Session. By default, Visualize uses only the recent conversation history to build the image prompt — it understands the scene from context. The "visualizeInstructions" boolean adds an additional input: when set to true, the character's instructions file is included in the Visualize context.
This is most useful for characters with a defined physical appearance described in their instructions. Without this setting, Visualize might generate plausible but generic images that don't match your character's look. With it, the image prompt generator can reference your character's appearance directly — hair color, clothing style, distinguishing features — and produce visuals that are consistent with the character you've defined. Enable it for any character whose instructions contain significant physical description.
Per-Project Agents
The "agents" array enables the agent system for this project. See Chapter 7 for complete documentation on the agent system. The value here is an array of agent ID strings:
{
"agents": [
"studio.memory-store",
"studio.character-stats",
"studio.quest-log"
]
}
Entries come in three forms, and they can be mixed freely in one array. A bare string is a built-in agent ID ("studio.memory-store"), a local JS/TS agent file, or — if it contains .subagent.md — a sub-agent shorthand. An object declares a composed instance: a composable implementation bound to a data file, or a sub-agent with wiring. See Chapter 7 for the composable lookup agents and Chapter 8 for sub-agent registration in full.
{
"agents": [
"studio.memory-store",
"my-tracker.ts",
"marcus.subagent.md",
{ "id": "char.cars-db",
"implementation": "studio.agent-db", "contextFile": ".cars.json" },
{ "id": "eve.lorekeeper",
"implementation": "studio.sub-agent",
"promptFile": "lorekeeper.subagent.md",
"history": "recent:10",
"model": "main",
"shared-agents": ["studio.history-search"],
"private-agents": ["studio.memory-store"] }
]
}
In order: a built-in by ID, a local JS/TS agent file, a sub-agent shorthand, a composed lookup instance, and a sub-agent object entry carrying per-instance overrides (history, model) and both grant lists.
Sub-agent object entries require "implementation": "studio.sub-agent" and a promptFile (relative to the project's agents/ directory); the composable lookup agents require contextFile instead. The config schema enforces this pairing, so the Config editor flags a promptFile on an agent-db entry as you type. The two grant lists — shared-agents (references to existing top-level instances) and private-agents (fresh, private declarations, full syntax including composed objects) — are documented with worked examples in Chapter 8. Per-instance overrides accept every sub-agent frontmatter field: description, history, model, contextFiles (which replaces the frontmatter list), maxTokens, maxRounds, temperature, topP, params, and debug.
The agent token caps — per-response and per-conversation budgets for agent-initiated model calls — live in Settings, not here. A project's config.json (and therefore any character package you receive) can select between your configured models with worker/main and spend within your caps, but it can never name an explicit model or raise the caps. See Chapter 8 for the metering rules.
Agent default files
Every agent stores its state in a per-conversation sidecar file. When a new conversation starts, the agent has no sidecar yet — by default it begins empty. Agent default files change this: they pre-seed every new conversation with a starting state you define, so you can ship a character with stats, memories, or quest state already populated from turn one.
The naming convention is {agentId}.agent.json, placed in the project root alongside config.json:
| Agent | Default file |
|---|---|
| studio.memory-store | studio.memory-store.agent.json |
| studio.character-stats | studio.character-stats.agent.json |
| studio.quest-log | studio.quest-log.agent.json |
| studio.event-tracker | studio.event-tracker.agent.json |
| studio.todo-list | studio.todo-list.agent.json |
| studio.npc-tracker | studio.npc-tracker.agent.json |
| studio.location | studio.location.agent.json |
| a sub-agent's private instance | eve.marcus.studio.memory-store.agent.json |
When a new conversation is loaded and an agent has no sidecar yet, Character Studio checks for the corresponding default file. If it exists, that content becomes the starting state for the conversation. Once the model writes to the agent for the first time, a conversation-scoped sidecar is created and the default file is no longer consulted for that conversation.
The easiest way to create a default file is from the Agent Store Panel. Configure the agent state the way you want new conversations to start — set initial stats, add starting memories, populate an NPC roster — then click the Save as Default button in the agent's panel header. This writes the current store to the default file immediately.
Agent default files are tracked by git, unlike conversation sidecar files which are excluded by the project's .gitignore. This means your starting state is part of the character's version history and travels with the project when you share or export it. When you publish to Venice and users start conversations with your character, their conversations seed from whatever default state you've defined.
Default files are plain JSON and can also be edited directly in a text editor or the Monaco editor if you need to set up complex initial state that's easier to write by hand than to configure through the panel. The structure matches whatever the agent uses internally — open an existing sidecar file to see the format, then copy and adapt it as your default.
Settings Hierarchy
When Character Studio determines what value to use for any given setting, it checks four sources in priority order, from highest to lowest:
- 1 Editor content — if a file is currently open in the editor, the editor's live content is used directly, bypassing the saved file entirely (we call this Live Edits). Changes you make take effect on the next message, even before you click Save.
- 2 config.json values — per-project overrides. Anything set here wins over the global setting for this project.
- 3 Global settings in localStorage — values you've set in the Settings dialog, which apply to all projects unless overridden.
- 4 Hardcoded defaults — the built-in fallback values for every setting, used when nothing else is configured.
This hierarchy means you can set a sensible global default (a model, a temperature) and then override it for specific characters without touching the global. It also means you rarely need to configure things at the project level unless your global default genuinely doesn't work for a particular character.
Live Edits makes the content of an open editor the source of truth, bypassing the saved file on disk. This is permanent Studio behavior, not a setting (older versions had a "Hot Reload" toggle for it; the toggle is gone and the behavior is always on). Unsaved editor content overrides the saved file, which means clearing the editor or closing a tab without saving reverts the character to the file's last saved version. Be aware of this when editing config.json: edits to the Config tab are live immediately, but only until you navigate away or close the tab without saving.
The .gitignore Tab
Your project uses git version control (managed through the History button in the toolbar), and the .gitignore tab controls which files are excluded from the version history. Character Studio creates a sensible default .gitignore when you create a new project, excluding conversation files (conversation_*.json, conversationList.json, and conversation_*.agent.json) so that your test chats don't accumulate in the character's commit history.
The reasoning for excluding conversations by default is practical: conversations are ephemeral test data, not part of the character definition. If you commit every conversation, your git history fills up with AI-generated test dialogue rather than meaningful changes to your instructions, system prompt, and configuration. Keeping them excluded keeps the history clean and useful. Also, storing them means permanently storing chats that might contain objectional material long-term.
You can add additional patterns to the .gitignore to exclude other files you don't want tracked. Common additions include large generated image files (images/*.png), temporary export files, and any draft files you create during development that aren't part of the publishable character. Edit the file directly in the .gitignore tab — it uses standard gitignore pattern syntax.
If you're using Character Studio for a character you intend to share or publish across multiple machines, consider adding to the .gitignore file early. Deciding up front what does and doesn't belong in version history saves you from having to clean up a cluttered git log later. Images in particular can make repositories large quickly.
Editing the .gitignore file is especially important if you plan to customize the image directory in the config.json.