# demos/
Recording tooling for the demo gifs in the project README.
## What's here
| File | Purpose |
|------|---------|
| `web.gif` | Browser UI demo, embedded in main README |
| `dump.gif` | Terminal `pgwt-server --dump` demo, embedded in main README |
| `dump.txt` | Raw text dump (101 lines) — reference output |
| `dump.tape` | VHS tape that renders `dump.gif` |
| `record-web.mjs` | Playwright script that drives + records the web UI |
| `record-web.sh` | Wrapper that boots `pgwt` locally and runs the Playwright script |
| `workload.sh` | The 60s mixed-wait workload — uploaded to the VM |
| `capture-trace.sh` | Provisions a Hetzner VM, builds, runs workload, downloads trace |
| `build-server-local.sh` | Builds `pgwt-server` on macOS / Linux for local replay |
| `Makefile` | Orchestrator: `make all` does everything |
| `PROMPTS.md` | LLM prompts for re-driving the web demo via an MCP browser |
## Reproducing the gifs from scratch
Prerequisites:
- **Hetzner Cloud account** + API token in `HCLOUD_TOKEN` env var
- SSH public key already uploaded to your Hetzner account (script picks the
one matching `~/.ssh/id_ed25519.pub` or `id_rsa.pub`)
- Local tools: `bash`, `rsync`, `ssh`, `scp`, `jq`, `curl`, `make`, `clang`/`gcc`
- For the `dump.gif`: [`vhs`](https://github.com/charmbracelet/vhs) (`brew install vhs`)
- For the `web.gif`: `node` + `npm`, `ffmpeg`, `go`
- macOS: `brew install lz4` (Linux usually has `liblz4-dev` packaged)
Then:
```bash
cd demos
export HCLOUD_TOKEN=... # your Hetzner Cloud API token
make all # provision VM → capture trace → render both gifs
make clean-vm # tear down the VM (saves ~€0.007/hr)
```
Or step by step:
```bash
make trace # ~3 min provisioning + 60s workload
make dump-gif # ~30 s
make web-gif # ~30 s (boots Playwright headed Chromium)
make clean-vm # delete the VM
```
The trace files land in `demos/local-trace/` (gitignored, ~63 MB compressed).
## Iterating on a gif without re-capturing
Once `demos/local-trace/` exists, you can re-render either gif freely without
spinning up a new VM:
```bash
# Tweak demos/dump.tape, then:
make dump-gif
# Tweak demos/record-web.mjs, then:
make web-gif
```
## How the trace was made
`workload.sh` runs on the VM in parallel with `pg_wait_tracer --daemon`:
1. **pgbench TPC-B** — 8 clients, 60s, scale 10 → Lock (row), WAL, ClientRead, LWLock:BufferContent
2. **5× injected `LOCK TABLE pgbench_branches IN EXCLUSIVE MODE` + 2s sleep** — seeds Lock:relation and forces queue contention
3. **3× heavy reads** (`SELECT count(*) FROM pgbench_accounts WHERE aid % 7 = 0`) — surfaces IO + buffer pin activity
Result: ~7M wait-event transitions, ~91 s wall clock, AAS 6.4. Wait class
breakdown (DB time): Client 32%, Lock 21%, Extension 16%, CPU 12%, LWLock 9%,
IO 7% (mostly WalSync).
## Why two recording mechanisms
- **VHS for `dump.gif`** — terminal output, no browser needed. Fast,
deterministic, tiny dependency footprint.
- **Playwright for `web.gif`** — the web UI is canvas-heavy (ECharts) and
needs a real Chromium. Playwright drives clicks/drags and records video;
`ffmpeg` post-processes to gif with a two-pass palette for clean colors.
## VM costs
Hetzner cpx22 = €0.0066/hr at the time of writing. A full `make all` run
(provision → capture → cleanup) takes ~5 minutes total. Don't forget
`make clean-vm` afterwards.
## Troubleshooting
- **"Local SSH key not found in Hetzner"** — upload your pubkey to Hetzner first:
`hcloud ssh-key create --name $(hostname) --public-key-from-file ~/.ssh/id_ed25519.pub`
- **`vhs` rendering blank** — your `~/.zshrc` may set odd PS1 that confuses VHS.
The tape uses `setopt interactive_comments` and absolute path to `pgwt-server`
to avoid PATH surprises.
- **Playwright fails to find tabs** — the UI's tab labels may have changed;
edit `record-web.mjs` (it uses `getByRole('tab', { name: ... })`).
- **`web.gif` too large** — bump `GIF_FPS` (env var, default 8) down or shrink
the `scale=` filter in the ffmpeg call inside `record-web.mjs`.