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) 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. ali: VTS PORT = 8001 MOUTH PARAM = “MouthOpen” VTuber mouth parameter TOKEN FILE = “vts token.json” class VTubeStudioClient: def \ init \ self : self.url = f"ws://localhost:{VTS PORT}" self.authenticated = False self.token = self.load token self.\ auth requested = False Prevent repeated auth requests self.\ connect def \ connect self : clean print f"\ cyan\ Connecting to VTube Studio on {self.url}...\ /cyan\ " self.ws = websocket.WebSocketApp self.url, on open=self.on open, on message=self.on message, on error=self.on error, on close=self.on close self.thread = threading.Thread target=self.ws.run forever, daemon=True self.thread.start def on open self, ws : clean print "\ green\ Connected to VTube Studio API\ /green\ " time.sleep 1 self.\ auth requested = False Reset on new connection if self.token: self.send { "apiName": "VTubeStudioPublicAPI", "apiVersion": "1.0", "requestID": "auth", "messageType": "AuthenticationRequest", "data": { "pluginName": "Local AI VTuber", "pluginDeveloper": "Bro77xp", "authenticationToken": self.token } } elif not self.\ auth requested: self.\ auth requested = True self.send { "apiName": "VTubeStudioPublicAPI", "apiVersion": "1.0", "requestID": "auth token", "messageType": "AuthenticationTokenRequest", "data": { "pluginName": "Local AI VTuber", "pluginDeveloper": "Bro77xp", "pluginIcon": "" } } def on message self, ws, message : try: msg = json.loads message mtype = msg.get "messageType", "" if mtype == "AuthenticationTokenResponse": token = msg\ "data"\ \ "authenticationToken"\ self.save token token clean print "\ green\ ✅ Token received and saved. Please restart the script.\ /green\ " self.authenticated = True elif mtype == "AuthenticationResponse": self.authenticated = True clean print "\ green\ Authenticated with VTube Studio \ /green\ " except Exception as e: clean print f"\ red\ Error parsing message:\ /red\ {e}" def on error self, ws, error : clean print f"\ red\ VTS WebSocket error:\ /red\ {error}" def on close self, ws, close status code, close msg : now = time.time if not hasattr self, '\ last close log' or now - self.\ last close log 30: self.\ last close log = now clean print "\ yellow\ VTS connection closed. Retrying in 5 seconds...\ /yellow\ " time.sleep 5 self.\ connect def send self, payload : try: if self.ws and self.ws.sock and self.ws.sock.connected: self.ws.send json.dumps payload else: now = time.time if not hasattr self, '\ last not connected log' or now - self.\ last not connected log 30: self.\ last not connected log = now clean print "\ yellow\ VTS not connected; could not send message suppressing for 30s \ /yellow\ " except Exception as e: clean print f"\ red\ Error sending to VTS:\ /red\ {e}" def trigger hotkey self, hotkey id, transition duration: float = 2.0 : """Trigger a hotkey with optional transition duration seconds for smooth animation fade.""" self.send { "apiName": "VTubeStudioPublicAPI", "apiVersion": "1.0", "requestID": f"hotkey\ {hotkey id}", "messageType": "HotkeyTriggerRequest", "data": { "hotkeyID": hotkey id, "transitionDuration": transition duration } } def inject parameters self, params: dict, request id: str = "inject" : """Inject multiple Live2D parameters in one request. params: {param id: value, ...} e.g. {"FaceAngleX": 15.0, "EyeOpenLeft": 0.5} """ values = \ {"id": k, "value": float v } for k, v in params.items \ self.send { "apiName": "VTubeStudioPublicAPI", "apiVersion": "1.0", "requestID": request id, "messageType": "InjectParameterDataRequest", "data": {"parameterValues": values} } def set mouth self, value : self.send { "apiName": "VTubeStudioPublicAPI", "apiVersion": "1.0", "requestID": "mouth", "messageType": "InjectParameterDataRequest", "data": { "parameterValues": \ { "id": MOUTH PARAM, "value": float value }\ } } def save token self, token : with open TOKEN FILE, "w" as f: json.dump {"token": token}, f def load token self : if os.path.exists TOKEN FILE : with open TOKEN FILE, "r" as f: return json.load f .get "token" return None Initialize VTS client vtube = VTubeStudioClient Idle animation engine - autonomous head/eye/brow movement when not speaking idle anim = IdleAnimationEngine vtube, lambda: tts active or tts generating or tts playing hara: VTS PORT = 8003 MOUTH PARAM = “MouthOpen” TOKEN FILE = “vts token1.json” class VTubeStudioClient: API NAME = "VTubeStudioPublicAPI" API VERSION = "1.0" def \ init \ self : self.url = f"ws://localhost:{VTS PORT}" self.ws = None self.thread = None self.authenticated = False self.token = self.load token self.hotkeys = {} self.\ last mouth = -1.0 self.\ last mouth time = 0.0 self.\ connect try: print "Authenticated:", self.authenticated except Exception: pass def \ connect self : console.print f"\ cyan\ Connecting to VTube Studio on {self.url}...\ /cyan\ " self.ws = websocket.WebSocketApp self.url, on open=self.on open, on message=self.on message, on error=self.on error, on close=self.on close self.thread = threading.Thread target=self.ws.run forever, daemon=True self.thread.start def on open self, ws : console.print "\ green\ Connected to VTube Studio API\ /green\ " time.sleep 1 if self.token: console.print "\ cyan\ Attempting to authenticate with stored token...\ /cyan\ " self.send request "auth", "AuthenticationRequest", { "pluginName": "Local AI VT3.0", "pluginDeveloper": "Bro77xp", "authenticationToken": self.token } else: console.print "\ cyan\ No stored token found. Requesting authentication token...\ /cyan\ " self.send request "auth token", "AuthenticationTokenRequest", { "pluginName": "Local AI VT3.0", "pluginDeveloper": "Bro77xp", "pluginIcon": "" } def on message self, ws, message : try: msg = json.loads message mtype = msg.get "messageType", "" if mtype == "AuthenticationTokenResponse": token = msg\ "data"\ \ "authenticationToken"\ self.save token token console.print "\ green\ ✅ Token received and saved. Authenticating...\ /green\ " self.send request "auth", "AuthenticationRequest", { "pluginName": "Local AI VT3.0", "pluginDeveloper": "Bro77xp", "authenticationToken": token } elif mtype == "AuthenticationResponse": self.authenticated = True console.print "\ green\ Authenticated with VTube Studio \ /green\ " except Exception as e: console.print f"\ red\ Error parsing message:\ /red\ {e}" def on error self, ws, error : console.print f"\ red\ VTS WebSocket error:\ /red\ {error}" def on close self, ws, close status code, close msg : console.print "\ yellow\ VTS connection closed. Retrying in 5 seconds...\ /yellow\ " time.sleep 5 self.\ connect def save token self, token : with open TOKEN FILE, "w" as f: json.dump {"token": token}, f def send request self, request id: str, message type: str, data: dict | None = None : if not self.is connected : return False payload = { "apiName": self.API NAME, "apiVersion": self.API VERSION, "requestID": request id, "messageType": message type, } if data: payload\ "data"\ = data try: self.ws.send json.dumps payload return True except Exception as e: console.print f"\ red\ VTS Send Error:\ /red\ {e}" return False def is connected self : return self.ws and self.ws.sock and self.ws.sock.connected def load token self : try: if not os.path.exists TOKEN FILE : return None with open TOKEN FILE, "r", encoding="utf-8" as f: data = json.load f return data.get "token" or data.get "authenticationToken" except Exception as e: console.print f"\ yellow\ Could not load token: {e}\ /yellow\ " return None