Starting State
Use the vizij-web workspace root:
cd /home/chris/Code/Semio/vizij_ws/vizij-web
pnpm run dev:vizij-standaloneThis walkthrough uses apps/vizij-standalone, the maintained desktop runtime shell for first deployment work.
Preflight
Before you run the standalone path, confirm these assumptions:
pnpm installhas already completed successfully invizij-web,- your machine can already launch the Tauri dev shell for
vizij-standalone, - you have one known-good bundled GLB available if you do not want to rely on the file picker,
- if you plan to launch fullscreen on a specific monitor, you can inspect displays first from the app-local README.
If the desktop shell itself does not start, stop here and use vizij-web/apps/vizij-standalone/README.md as the implementation authority before debugging runtime behavior.
What This First Deployment Must Prove
For this route, deployment means more than a face rendered.
The standalone surface has to make these things true:
- a face can be loaded into the runtime endpoint
- runtime status is visible
- a local control surface is exposed
- an operator on the same host can connect and drive the face
That is the bar for a first believable deployment endpoint.
Current deployable floor:
vizij-standalone plus one same-host control path.
This page is not yet claiming LAN-first, ROS-first, or multi-device deployment as the maintained baseline.
Walkthrough
1. Launch the standalone app in development
Run:
pnpm run dev:vizij-standaloneIf the app starts without a face already loaded, use the file picker to choose a bundled GLB or launch again later with --glb.
Expected result:
- the app opens as a desktop runtime surface rather than a browser-only demo
- if no model is loaded, the app makes that state explicit
- once a model is loaded, the runtime transitions into a visible active state
An explicit no model loaded state is part of healthy deployment behavior, not a failure in itself.
2. Load a face and wait for the runtime to settle
Use one of these paths:
- interactive file picker in the desktop app
- a CLI launch with a known GLB path
Example CLI form from the app README:
pnpm --filter vizij-standalone dev -- -- --glb /path/to/avatar.glb --fullscreenFor the full list of CLI options — display selection, port override, web control panel toggle, speech flags, and ROS2 surface options — see the vizij-standalone README.
Expected result:
- the face loads without the app hiding its intermediate states
- runtime health becomes visible in the app UI
- the surface now looks like an endpoint that can be operated, not only inspected
3. Confirm the local control endpoint
The default local control endpoint is:
ws://127.0.0.1:9000Unless disabled, the built-in browser control panel is served on the same port over HTTP:
http://127.0.0.1:9000The server binds to 0.0.0.0, so the control panel is also reachable from any device on the same network. If you want to drive the face from a phone or another machine, open:
http://<machine-ip>:9000No extra tooling or installation required on the controlling device.
What to confirm:
- the app surfaces the port or connection state clearly
- the runtime is ready before you expect remote or browser-side control to work
- the browser control panel is reachable from a phone or another machine on the same network
This is the maintained current truth for the first deployment route. Only expose the port on trusted networks — the server does not restrict access beyond the Arora exclusive-client policy.
4. Prove that operator control reaches the face
Use the built-in control panel, a phone on the same network, or another same-host client to drive one visible change.
Expected result:
- the client can connect to the standalone endpoint
- the face responds to control input
- the runtime remains healthy while the control path is active
At that point, the app has crossed from demo-shaped to deployment-shaped.
5. Inspect the code layers that make deployment work
Open:
vizij-web/apps/vizij-standalone/README.mdvizij-web/apps/vizij-standalone/src/App.tsxvizij-web/apps/vizij-standalone/src/hooks/useWebSocketSync.tsvizij-web/apps/vizij-standalone/src-tauri/src/lib.rs
What each layer owns:
App.tsxmounts the runtime-react surface in the desktop shelluseWebSocketSync.tsbridges runtime inputs and transport statesrc-tauri/src/lib.rsowns the CLI flags and endpoint startup
That separation is why this is a deployment surface and not just a richer player app.
Persisting Speech Keys with .env
Passing --deepgram-key, --openai-key, and --api-url on every launch is tedious. The standalone app reads those values from a .env file in src-tauri/ that Tauri loads automatically at dev and build time.
Where the file lives
apps/vizij-standalone/src-tauri/.envCopy the provided example to get started:
cp apps/vizij-standalone/src-tauri/.env.example apps/vizij-standalone/src-tauri/.envThen fill in your keys:
VITE_API_URL="https://us-central1-semio-vizij.cloudfunctions.net/api"
VITE_DEEPGRAM_API_KEY=your_deepgram_key_here
VITE_OPENAI_API_KEY=your_openai_key_hereWhat each variable does
| Variable | Purpose |
|---|---|
VITE_API_URL | TTS API base URL. Defaults to the hosted Semio endpoint. Override for a local TTS server. |
VITE_DEEPGRAM_API_KEY | Deepgram STT key. Required for microphone input when speech is enabled. |
VITE_OPENAI_API_KEY | OpenAI key. Required for conversation speech mode. |
The VITE_ prefix is required. Tauri forwards those variables to the Vite frontend where the speech hooks read them. Variables without that prefix are not visible to the React layer.
Precedence
CLI flags always win. The resolution order for each speech value is:
- CLI flag (
--deepgram-key,--openai-key,--api-url,--speech-mode) .envvariable or localStorage value- Bundle
speechConfigmetadata - Hook default
If speech looks half-configured, check both the .env file and the CLI flags before debugging the runtime layer.
The file is gitignored
src-tauri/.env is listed in the workspace .gitignore. Do not add it to version control. Use src-tauri/.env.example as the committed template.
The Smallest Useful Operator Checklist
Call the deployment healthy only when all of these are true:
- a face is loaded
- runtime status is visible and healthy
- the WebSocket endpoint is available
- a same-host control client can connect
- the face responds to control input predictably
If any one of those is missing, keep treating the setup as unfinished.
Pre-Flight And Post-Flight Checklist
Use this checklist when you want one compact pass/fail loop instead of rereading the whole walkthrough.
| Phase | Check | Pass signal | Fail clue |
|---|---|---|---|
| pre-flight | start the app with pnpm run dev:vizij-standalone or pnpm --filter vizij-standalone dev -- -- --glb /path/to/avatar.glb --fullscreen | desktop window opens and stays up | process fails early or the port is already in use |
| pre-flight | load one face | the face becomes visible or the app leaves No model loaded | file picker returns but the app stays empty or enters an error state |
| pre-flight | confirm endpoint identity | the app reports the local control port and the default route is still ws://127.0.0.1:9000 unless you changed --port | you are guessing the port or the app still looks like it is starting |
| post-flight | prove one operator control path | built-in web control panel connects (from same host or phone/LAN at http://<machine-ip>:9000) and the face changes visibly | the client connects but no visible change reaches the face |
| post-flight | prove recoverability | reset returns the face toward its defaults and the endpoint stays healthy | the face stays stuck or recovery leaves the endpoint in an unclear state |
Fast Recovery If It Fails
Use these shortcuts:
- if the app opens with no face, use the file picker or relaunch with
--glb - if the face loads but control does not connect, verify the port and local endpoint state first
- if the runtime appears unhealthy, use Validation Checkpoints
- if the transport or control path is failing, use Troubleshooting Matrix
Recommended Next Steps
Continue to Operator and Deployment Model if you want the conceptual model behind this endpoint.
Then continue to Deployment Checks and Recovery for the first operational validation loop.