🔑 Enter API Key

Your API key to access Hermes

H E R M E S  ·  CONCEPT ONE · LIVING

HERMES

LIVE · WATCHING THE DECK
Hermes the evening watch
swipe ·
she becomes her
Good evening, Clark. I have the watch tonight.
Hermes
incoming announcement · Telegram
later
answer
Hermes
Caretaker reports: all gates green. Nothing needs you yet.
concept v0.2 · living portrait engine · personas swap art + voice · local assets only
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("sw.js").catch(()=>{}); } const P = { flagship:{name:"Hermes",role:"the evening watch",img:"hermes_flagship.png",voice:"LILY_V3_smooth_stab55.mp3",living:true, hello:"Good evening, Clark. I have the watch tonight."}, vesper:{name:"Vesper",role:"chief of staff",img:"vesper_v2.png",voice:"voice_professional.mp3", hello:"Vesper. Three items need you today — shall we begin?"}, nova:{name:"Nova",role:"the wild spark",img:"nova_v2.png",voice:"voice_playful.mp3", hello:"Nova! Two good ideas, one terrible idea. Choose fast."}, sakura:{name:"Sakura",role:"serene precision",img:"persona_japan.png",voice:"voice_japan.mp3", hello:"Konnichiwa. Your day, in perfect harmony."}, beatrix:{name:"Beatrix",role:"the socialite",img:"persona_british.png",voice:"voice_british.mp3", hello:"Hello, darling. Terribly clever, awfully discreet."}, valentina:{name:"Valentina",role:"fuego y corazón",img:"persona_latina.png",voice:"voice_latina.mp3", hello:"Hola, mi amor. I run your day with fire."}, riot:{name:"Riot",role:"skater chaos gremlin",img:"persona_skater.png",voice:"voice_skater.mp3", hello:"Yo! Boring stuff's done. Let's go."}, adaeze:{name:"Adaeze",role:"the queen",img:"persona_queen.png",voice:"voice_queen.mp3", hello:"Welcome. Nothing escapes my sight."}, sigrid:{name:"Sigrid",role:"the shieldmaiden",img:"persona_valkyrie.png",voice:"voice_valkyrie.mp3", hello:"Hail. I stand watch over your day."}, priya:{name:"Priya",role:"grace in silk",img:"persona_india.png",voice:"voice_india.mp3", hello:"Namaste. Your day, arranged like flowers."}, lilith:{name:"Lilith",role:"the night witch",img:"persona_goth.png",voice:"voice_goth.mp3", hello:"Another midnight. I've brewed the remedy."} }; const tb=document.getElementById("thumbs"); Object.keys(P).forEach(k=>{ const d=document.createElement("div"); d.className="thumb"; d.dataset.p=k; d.title=P[k].name; d.style.backgroundImage=`url(${P[k].img})`; tb.appendChild(d); }); tb.addEventListener("click",e=>{const t=e.target.closest(".thumb"); if(t) apply(t.dataset.p);}); let cur="flagship"; function apply(p){ cur=p; const d=P[p]; document.getElementById("faceImg").style.backgroundImage=`url(${d.img})`; const vid=document.getElementById("faceVideo"); if(d.living){ vid.style.display="block"; vid.play().catch(()=>{}); document.getElementById("faceImg").style.display="none"; } else { vid.style.display="none"; vid.pause(); document.getElementById("faceImg").style.display="block"; } document.getElementById("pname").textContent=d.name; document.getElementById("prole").textContent=d.role; document.getElementById("cface").src=d.img; document.getElementById("cname").textContent=d.name; document.querySelectorAll(".thumb").forEach(t=>t.classList.toggle("on",t.dataset.p===p)); addHer(d.hello); } document.querySelectorAll(".thumb").forEach(t=>t.onclick=()=>apply(t.dataset.p)); function speak(){ new Audio(P[cur].voice).play(); addHer("▸ speaking in her own voice…"); } function showCall(){ document.getElementById("call").classList.add("on"); new Audio("voice_call.mp3").play(); } function hideCall(){document.getElementById("call").classList.remove("on")} function answer(){ hideCall(); addHer("Line answered — she's already taking notes for you."); } let toastT; function shapeshift(){ const v=document.createElement("video"); v.src="shimmer_personas.mp4"; v.autoplay=true; v.playsInline=true; v.style.cssText="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:25"; document.querySelector(".stage").appendChild(v); v.onended=()=>v.remove(); addHer("✦ One of me for every mood of yours. Who's your Hermes?"); } async function demo(){ if(window.__demoRunning) return; window.__demoRunning=true; const steps=[ [200 ,()=>addHer("Hello — I'm Hermes. Clark's assistant. You've met his work; this is what it looks like when it has a face.")], [4200,()=>speak()], [10500,()=>{showCall();}], [16500,()=>{addHer("Calls, notifications, schedules — I screen and summarize everything.");}], [19500,()=>{shapeshift();}], [25500,()=>{apply('professional');}], [30500,()=>{apply('playful');}], [34500,()=>{apply('flagship');}], [39500,()=>addHer("One assistant, any persona. The body is the brand — 'Who's your Hermes?'")], [42000,()=>{window.__demoRunning=false;}] ]; steps.forEach(([t,fn])=>setTimeout(fn,t)); } function showToast(){ const t=document.getElementById("toast"); t.classList.add("on"); clearTimeout(toastT); toastT=setTimeout(()=>t.classList.remove("on"),4200); } function addHer(text){ const c=document.getElementById("chat"); const d=document.createElement("div"); d.className="msg her"; d.textContent=text; c.appendChild(d); c.scrollTop=c.scrollHeight; } const API_URL = ""; const API_KEY = localStorage.getItem("hermes_api_key") || ""; function chat(){ const i=document.getElementById("say"); const v=i.value.trim(); if(!v)return; const c=document.getElementById("chat"); const m=document.createElement("div"); m.className="msg me"; m.textContent=v; c.appendChild(m); i.value=""; addHer("Thinking..."); fetch(API_URL+"/chat", {method:"POST", headers:{"Content-Type":"application/json", "X-API-Key":API_KEY}, body:JSON.stringify({message:v, persona:currentPersona||"hermes"})}) .then(r=>r.json()) .then(d=>{ addHer(d.response); speak(d.response); }) .catch(e=>addHer("Error: "+e)); } let ttsAudio = null; async function speak(text){ try{ const ttsResp = await fetch("/synthesize", { method:"POST", headers:{"Content-Type":"application/json", "X-API-Key":API_KEY}, body:JSON.stringify({text: text.substring(0,500), voice: "en-US-JennyNeural"}) }); if(!ttsResp.ok) throw new Error("TTS failed"); const blob = await ttsResp.blob(); const url = URL.createObjectURL(blob); ttsAudio = new Audio(url); // Connect to analyser for lip-sync if(!audioCtx){ audioCtx = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioCtx.createAnalyser(); analyser.fftSize = 256; } const source = audioCtx.createMediaElementSource(ttsAudio); source.connect(analyser); analyser.connect(audioCtx.destination); ttsAudio.play(); drawLipSync(); // Start lip-sync animation }catch(e){ console.log("TTS failed, using fallback voice.mp3:", e); // Fallback to static voice.mp3 const persona = currentPersona || "hermes"; const voiceFile = (P[persona] && P[persona].voice) ? P[persona].voice : "voice_flagship.mp3"; const fallback = new Audio("assets/" + voiceFile); fallback.play().catch(()=>{}); } } var currentPersona = "hermes"; /* ORIGINAL_FUNCTION{ const i=document.getElementById("say"); const v=i.value.trim(); if(!v)return; const c=document.getElementById("chat"); const m=document.createElement("div"); m.className="msg me"; m.textContent=v; c.appendChild(m); i.value=""; setTimeout(()=>addHer(`Noted${v.endsWith("?")?" — and a good question":""}. In the full build, my real mind answers through this face.`),700); }