{"slug": "make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i", "title": "Make 2 ai vtubers talk to eachother in vtube studio With functioning token System For mouths (working code example for everyone cause i could not find this anywhere)", "summary": "A developer released a working code example enabling two AI-powered VTubers to converse in VTube Studio with a functioning token system for mouth animation, addressing a gap in available resources.", "body_md": "ali:\n\nVTS_PORT = 8001\n\nMOUTH_PARAM = “MouthOpen” # VTuber mouth parameter\n\nTOKEN_FILE = “vts_token.json”\n\nclass VTubeStudioClient:\n\n```\ndef \\__init_\\_(self):\n\n    self.url = f\"ws://localhost:{VTS_PORT}\"\n\n    self.authenticated = False\n\n    self.token = self.load_token()\n\n    self.\\_auth_requested = False  # Prevent repeated auth requests\n\n    self.\\_connect()\n\ndef \\_connect(self):\n\n    clean_print(f\"\\[cyan\\]Connecting to VTube Studio on {self.url}...\\[/cyan\\]\")\n\n    self.ws = websocket.WebSocketApp(\n\n        self.url,\n\n        on_open=self.on_open,\n\n        on_message=self.on_message,\n\n        on_error=self.on_error,\n\n        on_close=self.on_close\n\n    )\n\n    self.thread = threading.Thread(target=self.ws.run_forever, daemon=True)\n\n    self.thread.start()\n\ndef on_open(self, ws):\n\n    clean_print(\"\\[green\\]Connected to VTube Studio API\\[/green\\]\")\n\n    time.sleep(1)\n\n    self.\\_auth_requested = False  # Reset on new connection\n\n    if self.token:\n\n        self.send({\n\n            \"apiName\": \"VTubeStudioPublicAPI\",\n\n            \"apiVersion\": \"1.0\",\n\n            \"requestID\": \"auth\",\n\n            \"messageType\": \"AuthenticationRequest\",\n\n            \"data\": {\n\n                \"pluginName\": \"Local AI VTuber\",\n\n                \"pluginDeveloper\": \"Bro77xp\",\n\n                \"authenticationToken\": self.token\n\n            }\n\n        })\n\n    elif not self.\\_auth_requested:\n\n        self.\\_auth_requested = True\n\n        self.send({\n\n            \"apiName\": \"VTubeStudioPublicAPI\",\n\n            \"apiVersion\": \"1.0\",\n\n            \"requestID\": \"auth_token\",\n\n            \"messageType\": \"AuthenticationTokenRequest\",\n\n            \"data\": {\n\n                \"pluginName\": \"Local AI VTuber\",\n\n                \"pluginDeveloper\": \"Bro77xp\",\n\n                \"pluginIcon\": \"\"\n\n            }\n\n        })\n\ndef on_message(self, ws, message):\n\n    try:\n\n        msg = json.loads(message)\n\n        mtype = msg.get(\"messageType\", \"\")\n\n        if mtype == \"AuthenticationTokenResponse\":\n\n            token = msg\\[\"data\"\\]\\[\"authenticationToken\"\\]\n\n            self.save_token(token)\n\n            clean_print(\"\\[green\\]✅ Token received and saved. Please restart the script.\\[/green\\]\")\n\n            self.authenticated = True\n\n        elif mtype == \"AuthenticationResponse\":\n\n            self.authenticated = True\n\n            clean_print(\"\\[green\\]Authenticated with VTube Studio!\\[/green\\]\")\n\n    except Exception as e:\n\n        clean_print(f\"\\[red\\]Error parsing message:\\[/red\\] {e}\")\n\ndef on_error(self, ws, error):\n\n    clean_print(f\"\\[red\\]VTS WebSocket error:\\[/red\\] {error}\")\n\ndef on_close(self, ws, close_status_code, close_msg):\n\n    now = time.time()\n\n    if not hasattr(self, '\\_last_close_log') or now - self.\\_last_close_log > 30:\n\n        self.\\_last_close_log = now\n\n        clean_print(\"\\[yellow\\]VTS connection closed. Retrying in 5 seconds...\\[/yellow\\]\")\n\n    time.sleep(5)\n\n    self.\\_connect()\n\ndef send(self, payload):\n\n    try:\n\n        if self.ws and self.ws.sock and self.ws.sock.connected:\n\n            self.ws.send(json.dumps(payload))\n\n        else:\n\n            now = time.time()\n\n            if not hasattr(self, '\\_last_not_connected_log') or now - self.\\_last_not_connected_log > 30:\n\n                self.\\_last_not_connected_log = now\n\n                clean_print(\"\\[yellow\\]VTS not connected; could not send message (suppressing for 30s)\\[/yellow\\]\")\n\n    except Exception as e:\n\n        clean_print(f\"\\[red\\]Error sending to VTS:\\[/red\\] {e}\")\n\ndef trigger_hotkey(self, hotkey_id, transition_duration: float = 2.0):\n\n    \"\"\"Trigger a hotkey with optional transition duration (seconds) for smooth animation fade.\"\"\"\n\n    self.send({\n\n        \"apiName\": \"VTubeStudioPublicAPI\",\n\n        \"apiVersion\": \"1.0\",\n\n        \"requestID\": f\"hotkey\\_{hotkey_id}\",\n\n        \"messageType\": \"HotkeyTriggerRequest\",\n\n        \"data\": {\n\n            \"hotkeyID\": hotkey_id,\n\n            \"transitionDuration\": transition_duration\n\n        }\n\n    })\n\ndef inject_parameters(self, params: dict, request_id: str = \"inject\"):\n\n    \"\"\"Inject multiple Live2D parameters in one request.\n\n    params: {param_id: value, ...}  e.g. {\"FaceAngleX\": 15.0, \"EyeOpenLeft\": 0.5}\n\n    \"\"\"\n\n    values = \\[{\"id\": k, \"value\": float(v)} for k, v in params.items()\\]\n\n    self.send({\n\n        \"apiName\": \"VTubeStudioPublicAPI\",\n\n        \"apiVersion\": \"1.0\",\n\n        \"requestID\": request_id,\n\n        \"messageType\": \"InjectParameterDataRequest\",\n\n        \"data\": {\"parameterValues\": values}\n\n    })\n\ndef set_mouth(self, value):\n\n    self.send({\n\n        \"apiName\": \"VTubeStudioPublicAPI\",\n\n        \"apiVersion\": \"1.0\",\n\n        \"requestID\": \"mouth\",\n\n        \"messageType\": \"InjectParameterDataRequest\",\n\n        \"data\": {\n\n            \"parameterValues\": \\[{\n\n                \"id\": MOUTH_PARAM,\n\n                \"value\": float(value)\n\n            }\\]\n\n        }\n\n    })\n\ndef save_token(self, token):\n\n    with open(TOKEN_FILE, \"w\") as f:\n\n        json.dump({\"token\": token}, f)\n\ndef load_token(self):\n\n    if os.path.exists(TOKEN_FILE):\n\n        with open(TOKEN_FILE, \"r\") as f:\n\n            return json.load(f).get(\"token\")\n\n    return None\n```\n\n# Initialize VTS client\n\nvtube = VTubeStudioClient()\n\n# Idle animation engine - autonomous head/eye/brow movement when not speaking\n\nidle_anim = IdleAnimationEngine(vtube, lambda: tts_active or tts_generating or tts_playing)\n\nhara:\n\nVTS_PORT = 8003\n\nMOUTH_PARAM = “MouthOpen”\n\nTOKEN_FILE = “vts_token1.json”\n\nclass VTubeStudioClient:\n\n```\nAPI_NAME = \"VTubeStudioPublicAPI\"\n\nAPI_VERSION = \"1.0\"\n\ndef \\__init_\\_(self):\n\n    self.url = f\"ws://localhost:{VTS_PORT}\"\n\n    self.ws = None\n\n    self.thread = None\n\n    self.authenticated = False\n\n    self.token = self.load_token()\n\n    self.hotkeys = {}\n\n    self.\\_last_mouth = -1.0\n\n    self.\\_last_mouth_time = 0.0\n\n    self.\\_connect()\n\n    try:\n\n        print(\"Authenticated:\", self.authenticated)\n\n    except Exception:\n\n        pass\n\ndef \\_connect(self):\n\n    console.print(f\"\\[cyan\\]Connecting to VTube Studio on {self.url}...\\[/cyan\\]\")\n\n    self.ws = websocket.WebSocketApp(\n\n        self.url,\n\n        on_open=self.on_open,\n\n        on_message=self.on_message,\n\n        on_error=self.on_error,\n\n        on_close=self.on_close\n\n    )\n\n    self.thread = threading.Thread(target=self.ws.run_forever, daemon=True)\n\n    self.thread.start()\n\ndef on_open(self, ws):\n\n    console.print(\"\\[green\\]Connected to VTube Studio API\\[/green\\]\")\n\n    time.sleep(1)\n\n    if self.token:\n\n        console.print(\"\\[cyan\\]Attempting to authenticate with stored token...\\[/cyan\\]\")\n\n        self.send_request(\n\n            \"auth\",\n\n            \"AuthenticationRequest\",\n\n            {\n\n                \"pluginName\": \"Local AI VT3.0\",\n\n                \"pluginDeveloper\": \"Bro77xp\",\n\n                \"authenticationToken\": self.token\n\n            }\n\n        )\n\n    else:\n\n        console.print(\"\\[cyan\\]No stored token found. Requesting authentication token...\\[/cyan\\]\")\n\n        self.send_request(\n\n            \"auth_token\",\n\n            \"AuthenticationTokenRequest\",\n\n            {\n\n                \"pluginName\": \"Local AI VT3.0\",\n\n                \"pluginDeveloper\": \"Bro77xp\",\n\n                \"pluginIcon\": \"\"\n\n            }\n\n        )\n\ndef on_message(self, ws, message):\n\n    try:\n\n        msg = json.loads(message)\n\n        mtype = msg.get(\"messageType\", \"\")\n\n        if mtype == \"AuthenticationTokenResponse\":\n\n            token = msg\\[\"data\"\\]\\[\"authenticationToken\"\\]\n\n            self.save_token(token)\n\n            console.print(\"\\[green\\]✅ Token received and saved. Authenticating...\\[/green\\]\")\n\n            self.send_request(\n\n                \"auth\",\n\n                \"AuthenticationRequest\",\n\n                {\n\n                    \"pluginName\": \"Local AI VT3.0\",\n\n                    \"pluginDeveloper\": \"Bro77xp\",\n\n                    \"authenticationToken\": token\n\n                }\n\n            )\n\n        elif mtype == \"AuthenticationResponse\":\n\n            self.authenticated = True\n\n            console.print(\"\\[green\\]Authenticated with VTube Studio!\\[/green\\]\")\n\n    except Exception as e:\n\n        console.print(f\"\\[red\\]Error parsing message:\\[/red\\] {e}\")\n\ndef on_error(self, ws, error):\n\n    console.print(f\"\\[red\\]VTS WebSocket error:\\[/red\\] {error}\")\n\ndef on_close(self, ws, close_status_code, close_msg):\n\n    console.print(\"\\[yellow\\]VTS connection closed. Retrying in 5 seconds...\\[/yellow\\]\")\n\n    time.sleep(5)\n\n    self.\\_connect()\n\ndef save_token(self, token):\n\n    with open(TOKEN_FILE, \"w\") as f:\n\n        json.dump({\"token\": token}, f)\n\ndef send_request(\n\n    self,\n\n    request_id: str,\n\n    message_type: str,\n\n    data: dict | None = None\n\n):\n\n    if not self.is_connected():\n\n        return False\n\n    payload = {\n\n        \"apiName\": self.API_NAME,\n\n        \"apiVersion\": self.API_VERSION,\n\n        \"requestID\": request_id,\n\n        \"messageType\": message_type,\n\n    }\n\n    if data:\n\n        payload\\[\"data\"\\] = data\n\n    try:\n\n        self.ws.send(json.dumps(payload))\n\n        return True\n\n    except Exception as e:\n\n        console.print(f\"\\[red\\]VTS Send Error:\\[/red\\] {e}\")\n\n        return False\n\ndef is_connected(self):\n\n    return (\n\n        self.ws\n\n        and self.ws.sock\n\n        and self.ws.sock.connected\n\n    )\n\ndef load_token(self):\n\n    try:\n\n        if not os.path.exists(TOKEN_FILE):\n\n            return None\n\n        with open(TOKEN_FILE, \"r\", encoding=\"utf-8\") as f:\n\n            data = json.load(f)\n\n        return data.get(\"token\") or data.get(\"authenticationToken\")\n\n    except Exception as e:\n\n        console.print(f\"\\[yellow\\]Could not load token: {e}\\[/yellow\\]\")\n\n        return None\n```\n\n", "url": "https://wpnews.pro/news/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i", "canonical_source": "https://discuss.huggingface.co/t/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-system-for-mouths-working-code-example-for-everyone-cause-i-could-not-find-this-anywhere/177555#post_1", "published_at": "2026-07-07 18:46:08+00:00", "updated_at": "2026-07-07 19:08:28.203667+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-tools", "ai-agents", "developer-tools"], "entities": ["VTube Studio", "Bro77xp"], "alternates": {"html": "https://wpnews.pro/news/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i", "markdown": "https://wpnews.pro/news/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i.md", "text": "https://wpnews.pro/news/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i.txt", "jsonld": "https://wpnews.pro/news/make-2-ai-vtubers-talk-to-eachother-in-vtube-studio-with-functioning-token-for-i.jsonld"}}