Skip to main content
Glama
README.md
# Fortax Demo Shoot

Give it a product URL. Get back a narrated demo video in your own voice, with branded
intro and outro, on-screen callouts, zooms on every click, captions, and three shapes:
16:9 for YouTube, 1:1 for LinkedIn, 9:16 for Reels and Shorts.

It plans the walkthrough, drives the product in a real browser, records it, speaks every
step, and renders the result. Run it from the command line, or just ask your coding agent
— Claude Code, Codex, OpenCode or hiSAI — for "a demo video of my app".

## Demo

**English** — planned, recorded and narrated with no hands on the keyboard.

https://github.com/user-attachments/assets/f2575a30-87b2-4a34-91ba-6d1736192bf1

**हिंदी** — the same demo, narrated in Hindi.

https://github.com/user-attachments/assets/39644a8e-a057-4250-9f02-30267b64dd2d

Full-quality files in all three shapes, in both languages, are on the
[v0.1.0 release](https://github.com/amit-voais/fortax-demo-shoot/releases/tag/v0.1.0).

## How it works

```
brand    reads the site (and repo, if given)  -> name, tagline, colours, logo
plan     an LLM writes the walkthrough          -> steps, narration, callouts
voice    every line is spoken first             -> your cloned voice, or a preset voice
record   a real browser performs each step      -> waits while each line plays
compose  the recording is edited                -> zoom on each click, cursor, captions
film     the brand wraps the recording          -> intro, callouts, outro, 16:9 / 1:1 / 9:16
```

Every stage writes a file, so any stage can be re-run on its own. Voice lines that did
not change are reused.

Two things it refuses to do quietly:

- **A click that would land on the wrong element fails the step.** Before every click it
  checks what is actually under the pointer (sticky headers, overlays, a different
  control inside the same label) and stops rather than record a video that shows the
  wrong thing.
- **A voice line that fails stops the run** with the engine's real error, instead of
  producing a silent video. Without `--ref`, a failed line is first retried with
  Hidrogen's preset voice, and the log says so (`! s03: used Hidrogen's preset voice
  instead of omnivoice`). With `--ref`, your cloned voice is never silently swapped for a
  different one: pass `--allow-fallback` to accept Hidrogen's voice for those lines, or
  `--allow-silent` if captions-only is what you want.

## Install

```bash
git clone https://github.com/amit-voais/fortax-demo-shoot && cd fortax-demo-shoot
./install.sh
```

Needs Python 3.10+, `ffmpeg` and Node.js 18+. `install.sh` creates a virtual
environment, downloads Playwright's own Chromium (no Chrome install needed), links the
`demoshoot` command into `~/.local/bin`, and registers the skill and the MCP server with
every coding agent it finds on the machine. `./install.sh --dry-run` shows what it would
do without changing anything; `./install.sh --uninstall` removes the agent registrations,
the skill links and the `~/.local/bin/demoshoot` link (only if it still points into this
checkout). It leaves the repo's `.venv` in place; delete the folder to remove that too.

Voice cloning uses [OmniVoice](https://github.com/k2-fsa/OmniVoice) in its own Python
environment; point `OMNIVOICE_PYTHON` at that environment's `python`.

## Make a demo

```bash
demoshoot make --url https://your-product.com \
  --brief "Show how a new customer builds a plan and sees the yearly price" \
  --ref my-voice.wav -o out/
```

`my-voice.wav` is 5–15 seconds of you speaking clearly, one speaker, no music. Without
`--ref`, pick a preset voice: `--instruct "male, indian accent, middle-aged, moderate pitch"`.

`out/` then contains `demo-16x9.mp4`, `demo-1x1.mp4`, `demo-9x16.mp4`, `demo.srt`,
`poster.jpg`, `plan.json`, `brand.json`, and one editable video project per shape.

| Option | What it does |
|---|---|
| `--project DIR` | also read the product's source repo for its name, colours and logo |
| `--plan plan.json` | use a plan you wrote or edited instead of asking the LLM |
| `--aspects 16:9,9:16` | only render some shapes |
| `--no-film` | one plain `demo.mp4`, no branded scenes |
| `--llm ollama` | plan with a local model instead of the default |

**Hindi or Hinglish.** Write the plan in that language first, then record it:

```bash
demoshoot plan --url https://your-product.com --brief "…" --lang hi -o plan.json
demoshoot make --plan plan.json --ref my-voice.wav -o out-hi/
```

Captions render in Devanagari.

**Change a callout or the tagline after a run** by editing `out/plan.json` or
`out/brand.json`, then re-render the scenes without re-recording:

```bash
demoshoot film --screen out/work/screen.mp4 --brand out/brand.json \
  --plan out/plan.json --work out/work -o out/
```

Your agent can do the same with the `film` tool, which only needs the `out/` folder.

## Use it from a coding agent

After `install.sh`, ask your agent for a demo video. It has these tools:

| Tool | Use it for |
|---|---|
| `make_demo` | the whole thing in one call: URL in, finished videos out |
| `brand` | see what the tool reads as the product's name, tagline and colours |
| `plan_demo` | get a walkthrough plan to review or edit before recording |
| `shoot_demo` | record and narrate a plan into one plain video |
| `film` | re-render the branded scenes of a `make_demo` result after editing it |
| `doctor` | check what works on this machine |

Recording takes a few minutes and drives a real browser against the URL. It only
navigates, clicks, scrolls, hovers, types into visible fields and presses keys.

## Choices

| Stage | Option | Needs |
|---|---|---|
| Plan | `--llm hidrogen` (default) | `HIDROGEN_API_KEY` |
| | `--llm ollama` | a local model (`OLLAMA_MODEL`, default `qwen2.5:14b`) |
| | `--llm openai` | any OpenAI-compatible server (`OPENAI_BASE_URL`) |
| | `--llm claude` / `codex` / `hisai` | that CLI, logged in (see Safety) |
| Voice | `--voice omnivoice --ref me.wav` (default) | local OmniVoice (`OMNIVOICE_PYTHON`) |
| | `--voice omnivoice --instruct "…"` | a preset voice, no sample needed |
| | `--voice omnivoice-server` | `OMNIVOICE_URL` (OmniVoice on a GPU box) |
| | `--voice hidrogen` | `HIDROGEN_API_KEY` |
| | `--voice none` | captions only |

Valid `--instruct` words: male, female, child, teenager, young adult, middle-aged,
elderly, low pitch, moderate pitch, high pitch, very low pitch, very high pitch, whisper,
and the accents american, australian, british, canadian, chinese, indian, japanese,
korean, portuguese, russian (written as `indian accent`).

## Safety

- Plans that contain delete / pay / cancel-style steps are refused before the browser
  opens. `shoot`, `run` and `record` accept `--allow-destructive` for a plan you have
  checked yourself; `make` never does.
- If a click leaves the product's own domain, the browser goes back.
- Passwords never go in a plan. Write `{{secret:LOGIN_PASS}}` in the step and set
  `DEMOSHOOT_SECRET_LOGIN_PASS` in the environment; it is typed in, never logged. Only
  type secrets into password fields: a secret typed into an ordinary text field is
  visible in the video.
- Plans may only open `http://` and `https://` pages.
- The planner reads text from the product's pages. `--llm claude` runs Claude Code with
  no tools and no MCP servers; `hisai` runs without plugins in an empty folder but can
  still use its own tools; `codex` runs as configured. Prefer `hidrogen`, `openai` or
  `ollama` for sites you do not control.
- `demoshoot serve` (the job API) refuses to listen beyond localhost unless
  `DEMOSHOOT_TOKEN` is set, and does not accept secrets or the agent-CLI planners.
- Your voice sample stays on your machine when you use local OmniVoice.

## Licence

Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE). If you redistribute this or build
on it, keep the NOTICE file and credit **Fortax Demo Shoot by Amit Yadav (Fortax)**.