{"_id":"@benvdberge/demoreel","_rev":"3-3d44e298780fea9f96bdbb1ea59e5fe5","name":"@benvdberge/demoreel","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@benvdberge/demoreel","version":"0.1.0","keywords":["demo","demo-video","video","screencast","playwright","ffmpeg","documentation","gif","subtitles","transcript","cli","typescript","ai","agent","ai-agents"],"author":{"name":"Ben van den Berge"},"license":"MIT","_id":"@benvdberge/demoreel@0.1.0","maintainers":[{"name":"benvdberge","email":"benvdberge@gmail.com"}],"homepage":"https://github.com/benvdberge/demoreel#readme","bugs":{"url":"https://github.com/benvdberge/demoreel/issues"},"bin":{"demoreel":"dist/cli/index.js"},"dist":{"shasum":"00e56ff71f37ba03ac17b26f967eb1f67ff18507","tarball":"https://registry.npmjs.org/@benvdberge/demoreel/-/demoreel-0.1.0.tgz","fileCount":108,"integrity":"sha512-8DMusmmQv9X0A2UCIXg/w23cNb6t8CzbPzzKBCCUq64l8MmyO/DPb5f7xFI1rOrKefCRaguisG3BLRkUJP3a6Q==","signatures":[{"sig":"MEQCIFcyRn0rGV9Jy/yKpGj3MYQ35/VtOiwzi8r18Pz9tzWmAiAOrWYz+dryv4RUTa++RP55tnZhNLsONdC826Zm+gPhOA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":349400},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.12"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"ad8d85023027cf49675447bba61bdbaa91c17442","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","example":"cd examples/basic && node ../../dist/cli/index.js record","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest","postinstall":"node scripts/postinstall.mjs","smoke:docker":"bash scripts/docker-smoke.sh","prepublishOnly":"npm run build"},"_npmUser":{"name":"benvdberge","email":"benvdberge@gmail.com"},"deprecated":"Renamed: development continues as @pesuto/demotale. Install @pesuto/demotale instead. v0.1.0 of this package remains MIT.","repository":{"url":"git+https://github.com/benvdberge/demoreel.git","type":"git"},"_npmVersion":"11.19.0","description":"Record narrated demo videos of a local web app (Playwright + overlays + ffmpeg). Built for humans and coding agents.","directories":{},"_nodeVersion":"26.7.0","allowScripts":{"ffmpeg-static@5.3.0":true,"@playwright/test@1.62.1":true},"dependencies":{"ffmpeg-static":"^5.3.0","@playwright/test":"^1.56.1"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.4","typescript":"^5.9.3","@types/node":"^24.10.1"},"_npmOperationalInternal":{"tmp":"tmp/demoreel_0.1.0_1786400171904_0.5186745862030193","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-08-10T22:16:11.724Z","modified":"2026-08-17T15:07:29.571Z","0.1.0":"2026-08-10T22:16:12.071Z"},"bugs":{"url":"https://github.com/benvdberge/demoreel/issues"},"author":{"name":"Ben van den Berge"},"license":"MIT","homepage":"https://github.com/benvdberge/demoreel#readme","keywords":["demo","demo-video","video","screencast","playwright","ffmpeg","documentation","gif","subtitles","transcript","cli","typescript","ai","agent","ai-agents"],"repository":{"url":"git+https://github.com/benvdberge/demoreel.git","type":"git"},"description":"Record narrated demo videos of a local web app (Playwright + overlays + ffmpeg). Built for humans and coding agents.","maintainers":[{"name":"benvdberge","email":"benvdberge@gmail.com"}],"readme":"# demoreel\n\nYou type **one sentence to the AI you already use**, and demoreel is what that AI needs to record a\ndemo video of your running web app. Playwright plays a scripted click path, text overlays explain\neach step, and ffmpeg turns it into an mp4, a gif, a subtitle track and a transcript.\n\nThe scenario is a file in your repo. Record it locally, or let CI run the same command when the UI\nchanges, so the demo does not go stale the first time someone moves a button.\n\n<!-- Made by demoreel from examples/basic, which CI records on every push. -->\n![A demo recorded by demoreel](https://raw.githubusercontent.com/benvdberge/demoreel/main/docs/media/example.gif)\n\nThat gif was not screen-captured. It is `examples/basic/demo/parcel-desk.demo.ts`, recorded by this\npackage — the same path CI runs on every push.\n\n- **For the agent you already have.** After `init --agent`, five lines in `AGENTS.md` point at\n  `demoreel agent-guide`. The agent writes the scenario; demoreel is the motor and the feedback\n  (`check`, then one `record`).\n- **No microphone, no editor.** The explanation is text on screen, so a UI change costs you one\n  command instead of another afternoon of re-recording.\n- **In git, re-recorded by CI.** Same scenario, same `demoreel record`. The demo stays current\n  without a hand-filmed remake.\n- **Runs on your machine, free.** Playwright, Chromium and ffmpeg come with the package. No account,\n  no upload, no service.\n- **Honest by default.** `note()` puts \"seeded data, no real customer\" on screen and keeps it there,\n  and `redact` guarantees an element is never in frame. Both exist so the video is one you dare show\n  a customer.\n\n## Install\n\n```bash\nnpm i -D @benvdberge/demoreel\nnpx demoreel init --agent\n```\n\nNeeds Node 22.12 or later. That install downloads Chromium (postinstall) and a bundled ffmpeg\nfallback for macOS, Linux and Windows. A system ffmpeg on your PATH is used first when present.\nWithout any ffmpeg the recording still happens and you keep the webm; you just do not get an mp4.\n\nIf `npm i` ran with `--ignore-scripts`, or doctor reports a missing browser: `npx demoreel setup`.\n\nNot sure whether the machine is ready? `npx demoreel doctor` checks everything that can be missing,\nin ten seconds, and says what to do about each thing. It installs nothing itself.\n\n### For your coding agent\n\nAfter `init --agent`, five lines in `AGENTS.md` point at the real instructions. When you ask for a\ndemo, the agent should run this and follow it — not invent a scenario from memory:\n\n```bash\nnpx demoreel agent-guide\n```\n\nThe loop is: point the config at your app → write `demo/<thing>.demo.ts` → `npx demoreel check`\n(open the frames) → `npx demoreel record` once at the end.\n\n## Write a scenario\n\n`demo/tour.demo.ts`:\n\n```ts\nimport { test, expect } from '@benvdberge/demoreel';\n\ntest('A guided tour', async ({ page, demo }) => {\n  await page.goto('/');\n\n  await demo.card('Acme', 'From an incoming order to a shipped parcel');\n  await demo.hideCard();\n  await demo.note('Seeded data. No real customer.');\n\n  await demo.say('Someone picks the order. Nothing here happens by itself.');\n\n  await demo.step('The list comes from our own cache, and the screen says how old it is.', async () => {\n    await demo.spotlight(page.getByTestId('cache-age'), 2400);\n    await demo.clearSpotlight();\n  });\n\n  await demo.step('Opening it fetches the order live.', async () => {\n    await demo.click(page.getByRole('link', { name: 'Open' }));\n    await expect(page.getByRole('heading', { name: 'Order' })).toBeVisible({ timeout: 15_000 });\n  });\n});\n```\n\nIt is a Playwright test. Anything you can do in a test, you can do in a scenario.\n\n## Record it\n\n```bash\nnpx demoreel record\n```\n\nYou get `demo/output/a-guided-tour.mp4`, plus a `.gif`, a `.vtt` subtitle track and a markdown\ntranscript with timestamps.\n\n## The scenario API\n\n| | |\n| --- | --- |\n| `card(title, subtitle?, holdMs?)` / `hideCard()` | Full-screen title card |\n| `say(text, { badge?, hold? })` / `hide()` | A subtitle, held for as long as it takes to read |\n| `step(text, body?)` | A numbered step: show the text, let it be read, then act |\n| `spotlight(locator, holdMs?)` / `clearSpotlight()` | Scroll there, measure, frame it, dim the rest |\n| `click(locator)` | Move the pointer there in steps, pause, click |\n| `type(locator, text)` | Visible keystrokes |\n| `note(text)` / `note()` | A standing label in the corner |\n| `wait(label, promise)` | A named long wait; the transcript records how long it really took |\n| `chapter(title)` | A marker for the transcript and the subtitles |\n| `redact(locator)` | Take this element out of the picture |\n| `pause(ms)` | A pause, scaled by `speed` |\n\n## Configure it\n\n`demoreel.config.ts`:\n\n```ts\nimport { defineConfig } from '@benvdberge/demoreel';\n\nexport default defineConfig({\n  baseUrl: 'http://localhost:3000',\n  webServer: { command: 'npm start', url: 'http://localhost:3000', reuseExistingServer: false },\n  speed: 1,\n  redact: ['[aria-label=\"Account\"]', '.org-switcher'],\n  video: { formats: ['mp4', 'gif'] },\n  theme: { accent: '#38bdf8', captionPosition: 'top' },\n});\n```\n\nEvery key has a working default, so `defineConfig({})` is a valid config. A wrong one is a sentence\nnaming the key, not a stack trace, and an unknown key is an error rather than something silently\nignored.\n\n## Apps behind a login\n\nSome applications cannot be signed into with a token, only with a real browser session. Do it once:\n\n```bash\nnpx demoreel auth https://app.example.com/private --out .auth/session.json\n```\n\nA browser opens, you sign in, and the session is saved the moment it is real and then **verified in a\nfresh browser** before the command claims it worked. If the check fails the file is deleted rather\nthan left to break a recording twenty minutes in. Point `storageState` at it and later recordings\nneed nobody.\n\nThat file is a signed-in session for a real account, in plain JSON, on disk. Treat it as a\ncredential; `demoreel init` puts it in your `.gitignore` and says why.\n\n## In CI\n\n```yaml\n- run: npx playwright install --with-deps chromium\n- run: npx demoreel record\n- uses: actions/upload-artifact@v4\n  with: { name: demo, path: demo/output/* }\n```\n\n`--with-deps` installs the OS libraries Chromium needs on Linux runners. The package postinstall\nalready downloads the browser itself; on GitHub-hosted Ubuntu, system ffmpeg is usually present too.\n\n## Commands\n\n| | |\n| --- | --- |\n| `demoreel init` | Config, an example scenario, gitignore lines and npm scripts. Never overwrites. `--agent` also points AGENTS.md at the guide |\n| `demoreel agent-guide` | Print the one page of instructions for whatever writes the scenarios |\n| `demoreel setup` | Download Chromium (and verify ffmpeg) when postinstall was skipped |\n| `demoreel check [file]` | Play the click path without filming it. A frame per subtitle, and what the page held when a locator missed |\n| `demoreel record [file]` | Record and render. `--headed`, `--speed 1.4`, `--port 3100`, `--base-url <url>` |\n| `demoreel render` | Re-render what was recorded |\n| `demoreel join a.mp4 b.mp4 out.mp4` | Join two parts without re-encoding |\n| `demoreel auth <url>` | Save a browser session, once |\n| `demoreel doctor` | Check node, ffmpeg, browsers, config, the dev-server command and baseUrl in ten seconds. Installs nothing |\n\n`check`, `record`, `render` and `doctor` take `--json`. One envelope for all four,\n`{ demoreel, command, ok, problems, result }`, with `problems` naming the scenario, the step and the\nlocator where there is one. In JSON mode stdout carries the document and nothing else.\n\n## Documentation\n\n- [Getting started](https://github.com/benvdberge/demoreel/blob/main/docs/getting-started.md)\n- [Writing a scenario](https://github.com/benvdberge/demoreel/blob/main/docs/writing-a-scenario.md)\n- [Recipes](https://github.com/benvdberge/demoreel/blob/main/docs/recipes.md) — apps behind a login, two-part recordings, CI\n- [Traps](https://github.com/benvdberge/demoreel/blob/main/docs/traps.md) — what actually goes wrong when you record a browser, and why\n- [AGENTS.md](https://github.com/benvdberge/demoreel/blob/main/AGENTS.md) / [llms.txt](https://github.com/benvdberge/demoreel/blob/main/llms.txt) — for coding agents discovering this repo\n\n## What it does not do\n\nNo audio and no spoken commentary. That is a choice, not a gap: a narrated video has to be re-recorded\nby a person on every change, and text overlays roll out again by themselves. No hosting, no account,\nno upload. Everything happens on your machine.\n\n## License\n\nMIT © Ben van den Berge. The optional bundled FFmpeg binary (via `ffmpeg-static`) is GPL; see\n[NOTICE](NOTICE).\n","readmeFilename":"README.md"}