DocsConfiguration

Reference

Configuration reference

Every setting, with its default and range: how the face looks and moves (in Wireface Desktop and wireface-core), each desktop face's files and profile.json, environment variables, and the command line.

On this page
  1. Where settings live
  2. Face settings
  3. The desktop app's face.json
  4. Config files for wireface-core
  5. The desktop app's profile.json
  6. Environment variables
  7. Command line

Where settings live

Each desktop face keeps its files in a folder of its own. The original face (started without --face) uses data\ itself; a face started with --face NAME uses data\faces\NAME\, and one started with --config PATH keeps its other files in data\faces\ under the config file's name. data\ is in the app's folder: %LOCALAPPDATA%\Programs\Wireface\data when installed, or the clone's own data\ folder (the WIREFACE_DATA environment variable moves it).

FileWhat's in it
profile.jsonThe face's settings: its name, the agent, the voice, the room and the phone line (below). Created the first time a setting is saved.
face.jsonHow the face looks and moves (below). Changed from the Face tab.
state.jsonThe conversation to resume, for each agent.
secrets.jsonAPI keys pasted into Settings. Never part of profile.json, which is the file people share.
face_window.jsonWhere the face and panel windows are, and whether the panel is docked.
face\The picture the face wears (photo.jpg) and where its landmarks are (landmarks.json).
logs\app.log, shell.log, and the activity panel's activity-YYYY-MM-DD.jsonl. Private: they hold what the agent read and ran.
phone\The phone line's transcripts, messages, limits and bookings.json.
browser\The Chrome profile of the agent's own browser.

Shared by every face: data\sandbox (the default working folder), data\cache (the model lists), dataackups (a copy of each of the agents' files as it was before the Tools tab first changed it), and the speech models (deps\models, or MODELS_DIR).

wireface-core keeps nothing on disk: a face's settings are the config you give createFace, or a config file you load.

Face settings

How the face looks and moves. These settings are the engine's, so they're the same in wireface-core's config, the embed's URL parameters and the desktop app's face.json, apart from the differences listed below. Numbers outside their range are refused: wireface-core throws a ConfigError naming the setting, and the desktop app rejects the change.

Skin and wireframe

The normal way to choose a look is a skin plus skin_amount: how far the whole face is faded from the glowing wireframe to the skin.

SettingValuesDefaultWhat it does
skina skin id, or nullnull

The skin to wear, from the catalogue: mei, marcus, amara, walter, lucia, arjun, omar, ruth, robot, monster, dracula, or your own. null: the bare wireframe.

An id is lowercase letters, digits, - and _, up to 40 characters. wireface-core only.

skin_amount0 to 11

Wireframe (0) to skin (1): a cross-fade over the whole face. In between, the wireframe's lines, points and glow fade as the skin comes in, and the skin itself barely glows. A new value glides in.

The Wireframe ↔ Skin slider. It needs a skin on (or style sculpt); the bare wireframe ignores it.

stylehologram, sculpt, photohologram

Kept for compatibility. hologram is the glowing wireframe, sculpt a shaded head, photo the skin. Wearing a skin always shows it as photo; photo without a skin shows the wireframe.

To choose a look, wear a skin and set skin_amount instead.

Colours

SettingValuesDefaultWhat it does
preseta preset nameSets colors.primary, secondary and background at once (presets). It isn't kept itself: the three colours are.
colors.primary#rrggbb#22d3eeThe wireframe and its glow
colors.secondary#rrggbb#6366f1The rim light
colors.background#rrggbb#060b18A matching background for your page; the canvas itself stays transparent. In the desktop app, the floating panel's background.
colors.iris#rrggbb#4f86c6Eye colour (skins use their own)
colors.mouth#rrggbb#5c1018Inside the mouth
colors.skin#rrggbb#c9a48eThe sculpted head's skin
colors.lips#rrggbb#b06a6eThe sculpted head's lips

Look

SettingValuesDefaultWhat it does
glow0 to 20.6Bloom around the wireframe's lines. With a skin on, a tenth of it is used, so the picture doesn't bloom.
wire0 to 10.8How strong the wireframe's lines are
fill0 to 10.3The surface fill behind the lines
scanlines0 to 10.25Hologram scanlines
points0 to 10Dots on the mesh's corners
edge_fade0 to 10.5A soft edge around the face
eyestrue, falsetrueEyeballs with irises; false gives hollow wireframe eyes

Teeth

SettingValuesDefaultWhat it does
teeth_stylenormal, braces, vampire, goofy, sharp, turkeynormalThe photographic teeth a skin shows when the mouth opens. A catalogue skin brings its own: the robot has braces, the monster sharp teeth and Dracula vampire teeth.
teeth_width0.5 to 1.51How far the teeth reach towards the corners of the mouth
teeth_size0.5 to 1.51How tall the teeth are
An open mouth with each of the six teeth styles: normal, braces, vampire, goofy, sharp and turkey

Motion and lip sync

SettingValuesDefaultWhat it does
follow_mousetrue, falsetrueThe eyes and head follow the pointer
motion0 to 21How much the head moves on its own
expressiveness0 to 21How strongly expressions and gestures show
mouth_gain0.3 to 21How far the lip sync opens the mouth
sync_offset_ms-200 to 200, whole0Lip-sync timing: positive moves the mouth later than the sound, negative earlier
zoom0.5 to 21The face's size in its canvas. In the desktop app, its size in the floating panel.

Colour presets

The wireframe in each colour preset: cyan, ember, green, amber, violet, rose and ice, and cyan with points
Presetprimarysecondarybackground
cyan#22d3ee#6366f1#060b18
ember (claude in the desktop app)#d97757#f2c38f#14100c
green#34f5a0#0ea5e9#03110b
amber#fbbf24#f97316#140c02
violet#a78bfa#ec4899#0e0718
rose#fb7185#f59e0b#16060a
ice#e0f2fe#7dd3fc#0b1220

The desktop app's face.json

Wireface Desktop keeps each face's look in face.json, changed from the Face tab. It has the face settings above, with these differences:

  • No skin setting. The face always wears a picture: one of the eight portraits or your own photo, chosen on the Face tab or dropped on the face, and kept in the face's face\ folder. A new face wears Mei. photo records which (version, w, h and portrait); Wireface sets it, not you.
  • style is always photo. A face.json from an older version that says hologram is read as photo with skin_amount 0 (the wireframe), and sculpt as photo.
  • Other defaults: the colours are the claude preset, glow is 1, wire 0.85, fill 0.35 and scanlines 0.3.
  • The presets are claude, cyan, green, amber, violet, rose and ice.

And four settings of its own:

SettingValuesDefaultWhat it does
modefloating, windowedfloatingJust the face on the desktop, or the floating panel with captions and buttons
backgroundnone, solid, gradient, grid, starsgradientBehind the face in its floating panel
captionstrue, falsetrueSubtitles under the face in its floating panel
on_toptrue, falsetrueKeep the face above other windows

When it loads face.json, Wireface drops anything that's no longer valid and uses the default instead.

Config files for wireface-core

A wireface-core face's settings can live in a small JSON file. Every setting is optional; missing ones take their defaults. $schema points editors such as VS Code at the JSON Schema, for autocomplete and checking:

{
  "$schema": "https://raw.githubusercontent.com/compsmart/wireface-core/main/config/face.schema.json",
  "skin": "dracula",
  "skin_amount": 0.6,
  "teeth_style": "vampire",
  "preset": "rose",
  "motion": 0.7,
  "expressiveness": 1.3
}
import { createFace, validateConfig } from '@wireface/core';

const config = await (await fetch('/faces/dracula.json')).json();
const face = createFace(canvas, { config });   // checked: a ConfigError names the bad setting
validateConfig(config);                        // or check one yourself: returns the full config

The errors say exactly what's wrong: glow: 3 is outside 0..2, colors.primary: expected #rrggbb, unknown setting "glo". The schema is config/face.schema.json in wireface-core (or @wireface/core/face.schema.json), and config/examples/ has a few files to start from. The demo's Config panel shows any face you set up there as a file, with only what differs from the defaults.

In code, DEFAULTS holds every default, RANGES the limits of every number setting, PRESETS the presets, and STYLES and TEETH_STYLES the choices.

The desktop app's profile.json

Each desktop face's settings, apart from its look. It's created the first time a setting is saved, and it's easiest to change from the panel, which saves as you go. To edit it by hand, quit the face first. The copy button next to Config file on the Agent tab gives you its path. profile.example.json, next to the app, shows the shape.

  • A file that isn't valid JSON is reported, and the defaults are used until it's fixed. If the face saves over it meanwhile, the broken file is kept as profile.json.bad.
  • A setting that's no longer valid (a working folder that's gone, say) is dropped, with a warning in app.log, and its default used.
  • Changes made in the panel are checked the same way, and a refused change names the setting.

The face

KeyDefaultValuesWhat it does
name"Claude"1 to 40 charactersThe face's name: shown on it, and what you say to interrupt it
port8791 for the original face, 0 for others0 to 65535The face's local web server. 0 is any free port.

The original face takes its defaults for these from WIREFACE_NAME and WIREFACE_PORT.

The agent: backend

KeyDefaultValuesWhat it does
backend.type"claude"claude, opencode, codexThe agent behind the face
backend.cwddata\sandboxa folder that existsWhere the agent works. WIREFACE_CWD changes the default.
backend.modelnulla model name, or nullThe model; null leaves it to the agent
backend.permission_mode"default"default, plus acceptEdits and plan (Claude Code), own (OpenCode), read-only (Codex)When the agent asks first (permissions). Not used while permissions are skipped.
backend.skip_permissionsfalsetrue, falseNever ask before acting (skipping permissions)
backend.auto_starttruetrue, falseStart the agent session with the face
backend.tools.computertruetrue, falseClaude Code can see the screen and use the mouse and keyboard
backend.tools.browsertruetrue, falseClaude Code can use a Chrome window of its own
backend.tools.chrometruetrue, falseClaude Code can work in your Chrome (Claude in Chrome)
backend.options{}an objectFor the agent: cli_path (its executable) and cli_args (OpenCode and Codex) (details)

The voice: voice

KeyDefaultValuesWhat it does
voice.ontruetrue, falseSpeak replies. false: captions only, and no realtime voice.
voice.listeningfalsetrue, falseThe microphone is on when the face starts
voice.mode"realtime"realtime, classicA realtime speech-to-speech model talks with you, or the classic voice reads the replies
voice.realtime.provider"gemini"gemini, openaiGemini Live or OpenAI Realtime
voice.realtime.gemini.model"gemini-3.8-live"a model nameThe Gemini Live model
voice.realtime.gemini.voice"Puck"a voice nameIts voice
voice.realtime.gemini.api_key_env"GEMINI_API_KEY"a variable nameThe environment variable to read the key from (GOOGLE_API_KEY is tried too)
voice.realtime.openai.model"gpt-realtime-2.1"a model nameThe OpenAI Realtime model
voice.realtime.openai.voice"marin"a voice nameIts voice
voice.realtime.openai.api_key_env"OPENAI_API_KEY"a variable nameThe environment variable to read the key from
voice.realtime.narration"normal"quiet, normal, chattyHow often it reports progress (Updates)
voice.realtime.persona"self"self, relaySpeaks as the agent, or about it
voice.realtime.barge_infalsetrue, falseInterrupt it by talking (use headphones)
voice.realtime.fallbacktruetrue, falseThe classic voice reads the replies while the realtime voice can't connect; false: they show as text
voice.hearing.name_interrupttruetrue, falseSaying its name stops it talking, and it listens
voice.hearing.denoisetruetrue, falseReduce steady background noise before recognition
voice.hearing.focustruetrue, falseIgnore voices much quieter than yours
voice.engine"auto"auto, local, elevenlabsThe classic voice: ElevenLabs when a key is available, this PC only, or ElevenLabs first
voice.local.engine"pocket"pocket, kokoroThe voice on this PC (TTS_ENGINE sets the default)
voice.local.voice"marius"a voice nameWhich of the local model's voices
voice.elevenlabs.voice_idnulla voice id, or nullThe ElevenLabs voice; null: the first voice on the account
voice.elevenlabs.model_id"eleven_flash_v2_5"a model idThe ElevenLabs model
voice.elevenlabs.api_key_env"ELEVENLABS_API_KEY"a variable nameThe environment variable to read the key from (XI_API_KEY is tried too)

Rooms: room

KeyDefaultValuesWhat it does
room.namenullup to 40 letters, digits, . _ -, or nullThe room this face joins; null: it works alone. --room NAME joins one for a single run.
room.aliases[]up to 10 names, each up to 30 charactersOther ways of saying this agent's name, for speech recognition
room.lead"first"first, or an agent's nameWhoever answers first leads, or that agent always does

The phone line: phone

KeyDefaultValuesWhat it does
phone.enabledfalsetrue, falseThe phone line
phone.account_sidnullAC and 32 hex digitsThe Twilio account SID
phone.numbernulla phone numberThe Twilio number, in full with its country code
phone.auth_token_env"TWILIO_AUTH_TOKEN"a variable nameThe environment variable with the auth token (or paste it in Settings)
phone.ownernullup to 60 charactersWho the line agent works for ("calling on behalf of ...")
phone.default_prefix"+44"a country codeFor numbers written without one
phone.allow_countries["+44"]up to 40 country codesWhere texts and calls may go
phone.contacts{}up to 300, "name": "number"Who the agent may text and call by name
phone.texts"receptionist"receptionist, notify, offIncoming texts: answered, passed to you, or ignored
phone.calls"receptionist"receptionist, offIncoming calls: answered, or not
phone.public_urlnullan https:// address, or nullThe phone server's public address; null: a Cloudflare tunnel
phone.manage_numbertruetrue, falsePoint the number's calls here on start, and turn off Twilio's demo reply to texts
phone.check_every_s155 to 300Seconds between checks for new texts
phone.limits.texts_per_day200 to 500Texts your agent may send a day
phone.limits.calls_per_day100 to 200Calls your agent may make a day
phone.limits.replies_per_number_per_day200 to 200Texts the line agent answers from one number a day
phone.limits.call_minutes101 to 60The longest a call may last
phone.models.text"gemini-3.8-flash"a model nameThe model answering texts (an OpenAI one is used when there's only an OpenAI key)
phone.models.call_provider"gemini"gemini, openaiThe service for live calls
phone.models.call"gemini-3.8-live"a model nameThe live call model
phone.models.call_voice"Puck"a voice nameIts voice
phone.receptionist.businessnullup to 80 charactersThe business's name, as callers know it
phone.receptionist.about""up to 4,000 charactersWhat callers may be told: services, prices, address
phone.receptionist.bookingstruetrue, falseTake bookings
phone.receptionist.hours09:00-17:30 Monday to Fridaymon to sun: "HH:MM-HH:MM" or nullOpening hours; null is closed
phone.receptionist.slot_minutes305 to 240The length of an appointment
phone.receptionist.max_days_ahead601 to 365How far ahead bookings may be
phone.receptionist.per_caller_per_day21 to 20Bookings one caller may make a day

An example

{
  "name": "Claude",
  "port": 0,
  "backend": {
    "type": "claude",
    "cwd": "C:\\Users\\you\\projects\\something",
    "model": null,
    "skip_permissions": false,
    "permission_mode": "default",
    "auto_start": true,
    "options": {}
  },
  "voice": {
    "on": true,
    "listening": false,
    "mode": "realtime",
    "realtime": {
      "provider": "gemini",
      "gemini": { "model": "gemini-3.8-live", "voice": "Puck", "api_key_env": "GEMINI_API_KEY" },
      "openai": { "model": "gpt-realtime-2.1", "voice": "marin", "api_key_env": "OPENAI_API_KEY" },
      "narration": "normal",
      "persona": "self",
      "barge_in": false
    },
    "hearing": { "name_interrupt": true, "denoise": true, "focus": true },
    "engine": "auto"
  },
  "room": { "name": null, "aliases": [], "lead": "first" }
}

Older versions kept a single face's settings in data\settings.json. If the original face has no profile.json yet, its working folder, permission mode, model, voice settings and conversation are carried over from there.

Environment variables

VariableDefaultWhat it does
GEMINI_API_KEY, GOOGLE_API_KEYThe Gemini key: the realtime voice and the phone line. Also wireface-core's tools.voice.
OPENAI_API_KEYThe OpenAI key
ELEVENLABS_API_KEY, XI_API_KEYThe ElevenLabs key
TWILIO_AUTH_TOKENThe Twilio auth token
WIREFACE_NAMEClaudeThe original face's default name
WIREFACE_PORT8791The original face's default port
WIREFACE_CWDdata\sandboxThe default working folder
WIREFACE_DATAdata in the app's folderWhere the faces keep their files
WIREFACE_DEPSdeps in the app's folderThe installed runtimes, and the speech models unless MODELS_DIR is set
MODELS_DIRdeps\models (or B:\models if that folder exists)The speech and face models, shared by every face
SPEECH_DIRspeech in MODELS_DIRThe speech models on their own
TTS_ENGINEpocketThe default voice on this PC: pocket or kokoro
TTS_SPEED1.05How fast the voice on this PC speaks
KOKORO_SID0Which Kokoro speaker
ASR_THREADS6CPU threads for speech recognition
CLAUDE_CONFIG_DIR~\.claudeClaude Code's folder: its session files (Activity's Raw view), and its settings, MCP servers and skills (the Tools tab). It also moves ~\.claude.json
CODEX_HOME~\.codexCodex's folder: its config.toml, MCP servers and skills (the Tools tab)
OPENCODE_CONFIG_DIR~\.config\opencode (or opencode in XDG_CONFIG_HOME)OpenCode's global config and skills (the Tools tab)

Each key setting can name another variable to read (api_key_env, auth_token_env). A key pasted into Settings is used before any variable. On Windows, a user or system variable set while Wireface is running is picked up within seconds.

Command line

Wireface Desktop

Wireface.exe (installed), Wireface.cmd (a clone) and .venv\Scripts\python -m wireface all take the same options:

OptionWhat it does
--face NAMERun the face called NAME, with its own files in data\faces\NAME. Without it: the original face.
--config PATHRun the face configured by that profile.json; its other files go to data\faces\ under the file's name
--room NAMEJoin that room for this run
--browserDon't open the desktop window: print a URL to open in a browser
--versionPrint the version

From a clone, there are also:

RefreshModels.cmd                                 # download the Model settings' lists again (--face NAME for another face)
.venv\Scripts\python -m wireface.models --check   # which speech models are installed or missing (--load: load them too)
.venv\Scripts\python -m wireface.models --install # download what's missing (--with-kokoro: the Kokoro voice too)
scripts\setup.ps1                                 # install or repair everything (-WithKokoro)

wireface-core

npm start                     # the examples and demo on http://127.0.0.1:5173/ (PORT=8080 npm start for another port)
npm test                      # node --test: config, schema, assets, loading
python -m tools.voice "Hello there." --voice Kore -o assets/voices/hello     # text to a clip (GEMINI_API_KEY)
python -m tools.lipsync speech.wav --text "Hello there." -o speech.json      # any audio to a clip
python -m tools.lipsync --get-lexicon                                        # once: the pronunciation lexicon

The clip tools are explained in Making clips.