Starting State
Use the vizij-web workspace root:
cd path/to/vizij-webThis walkthrough uses apps/tutorial-fullscreen-face, which is the smallest maintained Vizij runtime app in the workspace.
What You Need
Install dependencies once:
pnpm installStart the tutorial app:
pnpm run dev:tutorial-fullscreen-faceOpen the local Vite URL that appears in the terminal.
Quick term bridge
Keep these four labels distinct while you work through the first run:
| Term | What it means on this page | What it does not mean yet |
|---|---|---|
face artifact | the bundled sample face being loaded | your own authored face |
runtime bundle | the face GLB, with its bundle, handed to VizijRuntimeProvider | the whole application shell |
app shell | the tutorial app that hosts the face and hooks | a deployed operator endpoint |
deployment endpoint | a later Deploy concern with an exposed control path | this first-run tutorial surface |
What Success Looks Like Up Front
Before you inspect any code, know what a healthy first run should look like:
- the page opens without a blank canvas or crash
- the face becomes visible after loading finishes
- holding an expression key fades that expression in, and releasing it fades it out
- pressing a viseme key moves the mouth
If the face never appears, stop and use Validation Checkpoints or Troubleshooting Matrix before continuing.
Walkthrough
1. Launch the maintained runtime tutorial
From vizij-web, run:
pnpm run dev:tutorial-fullscreen-faceWhen the browser opens, wait for the face to settle into its ready state.
Expected result:
- you briefly see a loading bar while the runtime downloads and the face loads
- the face appears centered on screen
- the app stops looking transitional and starts behaving like a live face surface
Current visual anchor:
Use this still to confirm the baseline face state before you test motion or hotkey-driven changes.
2. Prove that the face is live, not static
Move the pointer over the page to show the key hints, then do two checks immediately:
- hold one of the expression keys (
1–0,Q–P,A–J) and watch the expression fade in, then release it - press one of the viseme keys (
L,Z–M) and watch the mouth take that shape (Kplayssil, the mouth at rest)
Expected result:
- an expression fades in over about two seconds while its key is held, and back out when released
- a viseme plays once per key press
- repeated interactions continue working, which proves the face’s device is actively taking writes rather than showing a prerecorded asset
With ?autoplay added to the URL, the face’s own program also runs from load, moving the face between your key presses.
This is the first useful Vizij confidence test. A rendered face is not enough. The face has to respond.
Motion anchor:
The still screenshot above proves the settled ready state. This loop is the stronger proof that the surface is live.
3. Inspect the runtime skeleton in code
Open these files:
vizij-web/apps/tutorial-fullscreen-face/src/FaceApp.tsxvizij-web/apps/tutorial-fullscreen-face/src/hooks/useFaceHotkeys.ts
In FaceApp.tsx, find the three pieces that define the app:
export function FaceApp() {
return (
<>
<VizijCanvas style={CANVAS_STYLE} />
<VizijRuntimeProvider
glb={faceAssetUrl}
options={{ program: autoplay ? "auto" : "none" }}
>
<FaceRuntime />
</VizijRuntimeProvider>
</>
);
}What each piece is doing:
<VizijCanvas>is the page’s one canvas, on which@vizij/runtime’s Bevy view draws the faceVizijRuntimeProviderfetches the face GLB, loads the face as an Arora device, steps it every frame, and reports itsstatusFaceRuntimerendersVizijRuntimeFace, the slot the face draws over, and binds the hotkeys once the face is ready
If you understand those three pieces, you understand the core shape of the maintained Hello Face path.
4. Connect the visible behavior to the maintained hook
Open useFaceHotkeys.ts.
You are looking for two different control patterns:
- an expression key fades the face standard’s expression weight,
runtime.path("standard/vizij/expression/<name>"), up while held and back down when released - a viseme key calls the face’s
play_visememethod, which plays one mouth shape through the face’s lipsync envelope
The hook reads its bindings from the face standard’s profile, vizij-face, in the profile’s order, and assigns them to its 36 keys: the 27 expressions take 1–0, Q–P and A–J, and the first nine of the 15 visemes (sil, PP, FF, TH, DD, kk, CH, SS, nn) take K, L and Z–M; the other six visemes have no key. You do not need to memorize every line yet. You do need to notice that both behaviors are driven through the face’s own API, not through one-off DOM tricks.
Expected result:
- the hook explains why holding a key fades an expression in and out
- the hook explains why a viseme key moves the mouth once
- the code lines up cleanly with the behavior you already saw in the browser
5. Name the maintained runtime pattern
At this point, you should be able to say:
- this app loads one existing face GLB
- the runtime provider loads the face as a device and keeps track of readiness
- a small hook writes the face’s keys and calls its methods
- the face is drawn by the same Bevy view and runtime every other Vizij app builds on
That is the entire reason this app is the first maintained route through the guidebook.
Why This Page Matters
Hello Face is not trying to teach all of Vizij.
It is trying to remove the first doubt:
- can I run a real Vizij face locally
- can I make it do something visible
- can I find the code path that produced what I just saw
Once those are true, later control, integration, and deployment pages have something solid to build on.
What This Page Is Not Proving Yet
This page proves a live runtime surface. It does not yet prove:
- that you understand path semantics in detail,
- that you have an application integration shell of your own,
- that you have authored or customized the face,
- that you have a deployment endpoint an operator can drive.
Choose Your Next Route
The canonical next step is First Control Interactions.
Use one of these branch points after that:
| If your next goal is… | Open this next |
|---|---|
| understand the visible interactions before going deeper | First Control Interactions |
| get to a player shell and keep the fast route | Minimal Web Player after Control |
| start owning the face and its behavior | Tweak an Existing Face after Control |
Fast Recovery If It Fails
Use these shortcuts instead of guessing:
- if dependencies are missing or stale, rerun
pnpm install - if the page loads but the face never appears, use Validation Checkpoints
- if the face appears but does not respond, continue to First Control Interactions and compare the expected behavior there
- if the app shows an error state, use Troubleshooting Matrix
Recommended Next Steps
Continue to First Control Interactions.
That page uses the same app, but it slows down and explains what the two maintained interaction patterns are actually doing.