Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ There's no hosted click-to-try demo yet _(coming soon)_. But you can run the pla
machine in about a minute — see **[Try it in 60 seconds](#-try-it-in-60-seconds)** below. It's a little
sandbox where you load a character, click its animations, and type to make it talk.

> 🎬 _An animated preview (a character actually moving and talking) is on the way — it's generated from the
> live demo, which arrives in a later step._
![Genie moving and talking in the MASH playground, rendered entirely in the browser](assets/gifs/genie-speaking.gif)

_Genie, loaded from a `.acs` file and speaking in his authentic voice — running entirely in your browser._

---

Expand Down Expand Up @@ -60,7 +61,9 @@ scratch, zero knowledge assumed.
The original four — **Genie, Merlin, Peedy, Robby** — plus Office favorites like **Clippy** and **Rover**,
and any other character anyone ever made. If it's a `.acs` file, vivify aims to run it.

> 🖼️ _A picture gallery of the characters is coming once the demo-capture step lands._
![Genie, rendered in the browser by vivify](assets/screenshots/genie-portrait.png)

_The gallery grows as characters are captured — here's Genie. Load any `.acs` to meet the rest._

---

Expand Down
1 change: 1 addition & 0 deletions assets/gifs/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Captured GIFs of the running app land here (operator-generated). See scripts/capture/.
1 change: 1 addition & 0 deletions assets/screenshots/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Captured screenshots of the running app land here (operator-generated). See scripts/capture/.
6 changes: 4 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ path here for you.
New to all of this? You're in exactly the right place — start with **[What is this?](what-is-this.md)** and
we'll explain it from scratch.

![The MASH playground running in a browser — a character on stage with its animation grid](../assets/screenshots/genie-app.png)

---

## Pick your platform

Setting it up on your own computer? Start here — each guide is step-by-step (screenshots are coming in a
later update).
Setting it up on your own computer? Start here — each guide is step-by-step, with a screenshot of the
running app at the finish line.

| 🪟 Windows | 🍎 macOS | 🐧 Linux |
| --- | --- | --- |
Expand Down
22 changes: 16 additions & 6 deletions docs/characters.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
# Characters

> 🚧 **Coming soon.** This page lands in **Cycle 17**. It's a placeholder for now, so links
> pointing here already work — no dead ends.
The cast of Microsoft Agent — **Genie**, **Merlin**, **Peedy**, **Robby**, the infamous **Clippy**, and
any other character anyone ever made. If it's a `.acs` file, vivify aims to run it.

**What it'll cover:** a gallery of the classic characters, plus how to get your own `.acs` character files.
![Genie, rendered in the browser by vivify](../assets/screenshots/genie-portrait.png)

In the meantime:
_The gallery grows as characters are captured. Here's Genie — load any `.acs` to meet the rest._

- New to all of this? Start with **[What is this?](what-is-this.md)**.
- Want to try it right now? See the **[main README](../README.md)**.
## How to get your own `.acs` files

vivify ships **no** character files — they're Microsoft's, and you supply your own. The
**[Legal & assets](legal-and-assets.md)** page explains exactly where to find them and why we don't
bundle them. Once you have a `.acs`, drop it onto the playground (see any of the
[install guides](README.md)) and it runs.

## Where to next

- **New here?** Start with **[What is this?](what-is-this.md)** — zero knowledge assumed.
- **Want to set it up?** Pick your platform from the **[documentation home](README.md)**.
- **Where do the files come from?** See **[Legal & assets](legal-and-assets.md)**.

---

Expand Down
100 changes: 100 additions & 0 deletions docs/cycles/cycle-18-screenshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Cycle 18 — screenshots + GIFs of the running app (Playwright capture)

## Goal
Produce real screenshots and animated GIFs of vivify actually running — a character loaded, the
animation grid, the speech balloon, and Genie talking with his mouth moving — and wire them into the
docs pages that currently signpost "images coming." This is the **last planned docs cycle**.

**The hard constraint:** capturing the live app needs the running stack, which only exists on the
operator's machine — Docker for MASH, and (for the talking/lip-sync shots) the Wine + SAPI4 + TruVoice
voice container plus the operator's user-supplied `.acs` and voice files. The CI sandbox has none of
that and cannot run Wine or the authentic voice. So this cycle ships **the capture tooling + the docs
wiring**; the operator runs the script locally and commits the produced image files.

**Docs + capture-script only (tooling, not app code); CI stays green.**

## What CC builds (this PR)
1. **A Playwright capture script** — `scripts/capture/capture-docs.ts` (+ `gif.ts`, `README.md`) that
drives MASH at a configurable URL (default `http://localhost:8090`), uploads a `.acs` the operator
supplies, plays an animation, types into the balloon, triggers Speak, and saves stills (PNG) and
short GIFs (PNG-frame loop → `gifenc`). Parameterized + documented for a clean first local run.
2. **Docs wiring** — replace the "coming soon / images coming" signposts with real `![alt](path)`
references at the output paths, with good alt text, so the pages render the images once the files
land.
3. **ADR-0028** — records the load-bearing asset decision (screenshots/GIFs of the running app are
permitted documentation, distinct from committing source `.acs`/binaries; a deliberate, scoped
carve-out from [ADR-0006](../decisions/0006-permissive-license-no-bundled-ip.md)).

## What the operator runs (and commits)
1. Bring the stack up: `docker compose up` (MASH on `:8090`; the voice container on `:8080` is needed
only for the authentic talking/lip-sync shots — Tier 2 from the install pages).
2. Install the browser once: `pnpm capture:setup` (runs `playwright install chromium`).
3. Capture: `pnpm capture -- --acs /path/to/Genie.acs` (defaults: `--url http://localhost:8090`,
`--name genie`, output to `assets/`). The script writes the five files below.
4. `git add assets/screenshots assets/gifs && git commit` — the operator commits the actual images to
this branch before merge.

The `.acs` is supplied via `--acs` and **never committed** (`*.acs` is gitignored). The script bakes in
**no** download of any proprietary asset.

## Assets produced (default `--name genie`)
| Path | What it shows | Wired into |
| --- | --- | --- |
| `assets/screenshots/genie-app.png` | The whole MASH window: character on stage + animation grid + controls | install ×3 ("you should see"), docs landing, dev overview, README (60-seconds) |
| `assets/screenshots/genie-portrait.png` | Just the character (the `#stage` element) | characters gallery, README cast |
| `assets/screenshots/genie-speaking.png` | A still mid-speech (balloon up, mouth moving) | — (companion; reserved) |
| `assets/gifs/genie-animation.gif` | A representative animation playing | — (reserved for future) |
| `assets/gifs/genie-speaking.gif` | Genie talking with his mouth moving | README hero ("See it move"), what-is-this, dev quickstart |

Doc paths assume the default `genie` prefix. Capture a different character with `--name <x>` and the
files become `<x>-…`; update the doc paths or stick with `genie` for the wired pages. The gallery grows
by running the script per character.

## Honest boundary (flagged, not hidden)
- CC **cannot** run Wine / the authentic voice, and likely cannot launch a real browser in the sandbox,
so CC does **not** produce the real images. The PR lands scripts + wiring; the operator generates and
commits the images.
- Until the operator commits the files, the new `![…]` references **404 on GitHub** — expected. Merge
only after the images are committed to this branch.
- Playwright can only photograph the **browser** (the running app). The install pages' Docker/terminal
steps stay as written text; their image is the end-result app screenshot, so the old "screenshots for
every step" promise is corrected to that honest scope.
- The talking/lip-sync shots (`*-speaking.*`) need the voice container up. The animation GIF and the
app/portrait stills need only MASH. If Speak can't reach the voice server, the script logs a warning
and still saves what rendered.

## IP / asset hygiene
- Never commit `.acs` or voice binaries (enforced by `.gitignore`: `*.acs`, `*.wav`). The script reads
the `.acs` from an operator-supplied path and never writes it anywhere.
- Captured PNG/GIF of the **running app** (rendered character pixels in the demo) are documentation of
the app — permitted by ADR-0028. The captured images must not embed or expose the `.acs` source —
they show only the rendered character, which is the intended documentation.
- No proprietary fetch/download is baked into the scripts.

## Tooling notes
- New **root** devDependencies: `playwright`, `tsx`, `pngjs` + `@types/pngjs`, `gifenc`. Root scripts:
`capture` (`tsx scripts/capture/capture-docs.ts`) and `capture:setup` (`playwright install chromium`).
- `scripts/` is **not** a pnpm workspace (mirrors the existing `packages/acs/scripts/spike-dump.ts`
tsx pattern); it resolves deps from root `node_modules` and is **not** in the CI `typecheck` graph. It
is linted by `eslint .` and formatted by Prettier, so it must pass both.
- GIFs are built from a loop of element screenshots → `pngjs` decode → optional nearest-neighbor
downscale (default max width 480px) → `gifenc` 256-color quantize. No system `ffmpeg` dependency.

## Acceptance check
- `scripts/capture/capture-docs.ts` exists, is parameterized (`--url/--acs/--name/--out/--speak/
--animation/--smoke`), documented in `scripts/capture/README.md`, and uses the real MASH selectors
(`#file`, `#stage`, `#animations .anim`, `#speak`, `#speakBtn`, `#voiceUrl`, `#status`).
- Every "images coming" signpost listed above is replaced by a real `![alt](assets/…)` reference with
descriptive alt text, at the correct relative depth.
- ADR-0028 is written and linked from the cycle doc.
- `pnpm -r typecheck && pnpm -r test && pnpm lint && pnpm format` is green with the new deps and script.

## Verification
- **CI (CC):** the four checks above pass. Best-effort: `--smoke` mode against a dev MASH to validate
selectors/plumbing without a `.acs` — reported honestly if the sandbox can't launch a browser.
- **Operator:** run the four steps above; confirm the five files land in `assets/`, open the docs on
GitHub and see them render. Tell CC if a shot needs a specific character/animation loaded.

## Non-goals
Nothing after this (last planned docs cycle). No app/`@vivify/core` code change. No committing `.acs`
or binaries. No hosted demo. No merge — open a PR (base `main`) and stop.
39 changes: 39 additions & 0 deletions docs/decisions/0028-screenshots-of-running-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ADR-0028: Screenshots and GIFs of the running app are committable documentation

Status: Accepted · Date: 2026-06-21

## Context

[ADR-0006](0006-permissive-license-no-bundled-ip.md) forbids committing third-party IP: no `.acs`
files, no SAPI4/TruVoice binaries, no extracted Microsoft assets. The docs (README, install pages,
what-is-this, the developer pages, the characters page) signpost screenshots and GIFs of vivify
running — and a captured screenshot of the running demo contains **rendered character pixels** (e.g.
Genie), which originate from Microsoft IP. That sits in tension with ADR-0006, so the boundary needs to
be stated, not assumed.

## Decision

Permit committing **screenshots and GIFs of the running application** — the rendered character in the
MASH demo, its animation grid, balloon, and lip-sync — as documentation of what the software does.
These live under `assets/screenshots/` and `assets/gifs/`.

This is distinct from, and does not loosen, ADR-0006's hard rules:

- The **source `.acs` files and engine binaries are still never committed.** The capture script
(`scripts/capture/`) reads the operator's `.acs` by path only, never writes or bundles it, and bakes
in no download of any proprietary asset.
- A captured image must show only the **rendered character**, never expose or embed the `.acs` source.
- Images are produced by the operator from their own legally-sourced character file and committed by
them; CI never has the assets and never generates them.

## Consequences

- The docs can show the real product. A screenshot/GIF of a running app is ordinary product
documentation (comparable to any project documenting its UI), and is treated as such here.
- This is a deliberate, scoped carve-out — a documentation image of rendered pixels, not redistribution
of the character file or engine. If a specific character's owner objects, the relevant image can be
swapped for a different (operator-supplied) character without code changes.
- `.gitignore` continues to enforce `*.acs` and `*.wav`; PNG/GIF under `assets/` are intentionally not
ignored so the operator can commit them.
- Because the assets are operator-generated, the docs' image references 404 until those files are
committed — an accepted, temporary state, not a broken build.
5 changes: 3 additions & 2 deletions docs/developers/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ vivify is built in **small, reviewable cycles**. The working model, in brief (th
- **Conventional commits, one PR per cycle.** CI (typecheck + test + lint) must be green **and** the
diff reviewed before merge — never force-merged on green alone.

> 🚧 **Screenshots and GIFs are coming in the next cycle.** This page is words for now; a character
> actually moving and talking is worth more than a paragraph, and that's on the way.
![The MASH demo running — a character on stage beside its animation grid, built only on @vivify/core's public API](../../assets/screenshots/genie-app.png)

_The MASH demo (`apps/mash`) running on the public API — both the showcase and the worked example._

---

Expand Down
2 changes: 2 additions & 0 deletions docs/developers/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ agent.speak('Hello! I am alive in your browser.');

That's it — he's alive, and talking.

![A character loaded in the browser and speaking, driven by @vivify/core](../../assets/gifs/genie-speaking.gif)

### Why pass a provider at all?

`createAgent`'s **default** provider is the silent `StubTtsProvider`: the character animates, but you
Expand Down
6 changes: 4 additions & 2 deletions docs/install/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ There are **two tiers**, and you can do just the first:
> New to all of this? The 60-second overview is **[What is this?](../what-is-this.md)**. Stuck on a word?
> The **[Glossary](../glossary.md)** explains every term in plain English.

> 📸 _Screenshots for every step are coming in a later update — for now the steps are written out in full._
> 📸 _Curious what success looks like? There's a screenshot of the running app at the end of Step 4._

---

Expand Down Expand Up @@ -81,13 +81,15 @@ starts fast.) When you see it settle and keep running, it's ready. Leave this te

1. Open your web browser to **http://localhost:8090**.
2. You'll need a character file (a `.acs` file). vivify ships none — see **[where to get
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page, _coming soon_). Drag
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page). Drag
the `.acs` file onto the page.
3. Click any animation in the list to play it. Type a sentence and click **Speak**.

**That's it — it's alive!** 🎉 The character moves, shows its speech balloon, and talks using your
browser's voice.

![The MASH playground running at localhost:8090 — a character on stage with its animation grid](../../assets/screenshots/genie-app.png)

> To stop it: go back to the terminal and press **Ctrl + C**.

---
Expand Down
6 changes: 4 additions & 2 deletions docs/install/mac.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ There are **two tiers**, and you can do just the first:
> New to all of this? The 60-second overview is **[What is this?](../what-is-this.md)**. Stuck on a word?
> The **[Glossary](../glossary.md)** explains every term in plain English.

> 📸 _Screenshots for every step are coming in a later update — for now the steps are written out in full._
> 📸 _Curious what success looks like? There's a screenshot of the running app at the end of Step 4._

---

Expand Down Expand Up @@ -74,13 +74,15 @@ starts fast.) When you see it settle and keep running, it's ready. Leave this wi

1. Open your web browser to **http://localhost:8090**.
2. You'll need a character file (a `.acs` file). vivify ships none — see **[where to get
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page, _coming soon_). Drag
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page). Drag
the `.acs` file onto the page.
3. Click any animation in the list to play it. Type a sentence and click **Speak**.

**That's it — it's alive!** 🎉 The character moves, shows its speech balloon, and talks using your
browser's voice.

![The MASH playground running at localhost:8090 — a character on stage with its animation grid](../../assets/screenshots/genie-app.png)

> To stop it: go back to the Terminal and press **Control + C**.

---
Expand Down
6 changes: 4 additions & 2 deletions docs/install/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ There are **two tiers**, and you can do just the first:
> New to all of this? The 60-second overview is **[What is this?](../what-is-this.md)**. Stuck on a word?
> The **[Glossary](../glossary.md)** explains every term in plain English.

> 📸 _Screenshots for every step are coming in a later update — for now the steps are written out in full._
> 📸 _Curious what success looks like? There's a screenshot of the running app at the end of Step 4._

---

Expand Down Expand Up @@ -72,13 +72,15 @@ starts fast.) When you see it settle and keep running, it's ready. Leave this wi

1. Open your web browser to **http://localhost:8090**.
2. You'll need a character file (a `.acs` file). vivify ships none — see **[where to get
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page, _coming soon_). Drag
one](../legal-and-assets.md)** (and the **[Characters](../characters.md)** page). Drag
the `.acs` file onto the page.
3. Click any animation in the list to play it. Type a sentence and click **Speak**.

**That's it — it's alive!** 🎉 The character moves, shows its speech balloon, and talks using your
browser's voice.

![The MASH playground running at localhost:8090 — a character on stage with its animation grid](../../assets/screenshots/genie-app.png)

> To stop it: go back to the terminal and press **Ctrl + C**.

---
Expand Down
2 changes: 2 additions & 0 deletions docs/what-is-this.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Let's start from zero. No background needed.
show a little speech bubble with their words. vivify recreates them faithfully: the real pictures, the real
animations, even their original voices.

![Genie moving and talking in vivify, running in a web browser](../assets/gifs/genie-speaking.gif)

That's it. The rest of this page fills in the story, in case the words "Microsoft Agent" don't ring a bell
— and for a lot of people, they won't.

Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@
"test": "pnpm -r test",
"lint": "eslint .",
"format": "prettier --check .",
"format:write": "prettier --write ."
"format:write": "prettier --write .",
"capture": "tsx scripts/capture/capture-docs.ts",
"capture:setup": "playwright install chromium"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/node": "^22.10.2",
"@types/pngjs": "^6.0.5",
"eslint": "^9.17.0",
"gifenc": "^1.0.3",
"playwright": "^1.49.1",
"pngjs": "^7.0.0",
"prettier": "^3.4.2",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
"typescript-eslint": "^8.18.1",
"vitest": "^2.1.8"
Expand Down
Loading
Loading