# VulkanAnimated A live HUD demo: three `Observable`s drive `ProgressBar`s, three `Observable`s drive `Text` labels, and an FPS readout in the corner ticks every frame. Everything updates from a single `onUpdate` listener — no `Invalidate()` / `Redraw()` calls. ## What it shows - **`Observable` data flow**: change a value in `onUpdate`, the next frame's `RebuildFrame` re-emits the draw list with the new value automatically. No tree rebuild. - **`ProgressBar::bindValue(obs, lo, hi)`** — the bar fill normalises the observable's current value into 0..1 each frame. - **`Text::bind(observable)`** — the displayed string is sourced from the observable each frame, replacing any baked-in runs. - **Composition pattern**: a small lambda helper builds one HP/MP-style row (`Text` + `ProgressBar` inside an `HStack`) given the observables and a colour, then the row is dropped into the parent `VStack` like any other widget. - **Different update rates per observable**: `health` oscillates at 0.7 rad/s, `mana` at 1.3, `charge` advances linearly modulo 1 — visible proof that each observable updates independently. ## Run ```bash cd examples/VulkanAnimated crafter-build -r ``` You should see "Animated HUD" with three coloured bars (red HP, blue MP, yellow Charge) all moving at different rates, with the FPS readout in the top-right ticking once per frame. Click `Quit` to exit. ## Notes The whole tick handler is just: ```cpp EventListener tick(&window.onUpdate, [&](FrameTime ft){ t += ft.delta.count(); health = 0.5f + 0.5f * std::sin(t * 0.7f); mana = std::abs(std::sin(t * 1.3f)); charge = std::fmod(t * 0.3f, 1.0f); healthLabel = std::format("HP {:>3.0f} / 100", health.Get() * 100); // … }); ``` That's the entire animation system. There is deliberately no `Animation` / tween primitive in the library — drive observables from any source you like.