πŸŽ¬β€‚Recording a web app

How do I put a demo of my product in a deck or on a landing page without shipping a video of it?

A product demo in a deck is almost always an MP4, and an MP4 is the wrong format for a user interface. It is lossy on exactly the content it carries β€” small type, hairlines, flat colour β€” it is a fixed grid of pixels in a layout that is not, and it weighs megabytes a minute.

narro record captures the sources of the frame instead: the DOM as it stood, every mutation and input that followed, and the bytes of everything the page drew with. The result replays as real DOM.

npx @getnarro/cli record https://your.app --html demo.html

A window opens, you drive the flow, and pressing Enter writes the file. Open it and the recording plays: text you can select, sharp at any zoom, and the whole thing works offline.

Stopping it

The recording ends when you say so, and there are three ways to say it. All three save:

Do thisAnd
press Enter in the terminalthe browser closes and the file is written
press Ctrl+C in the terminalthe same thing happens. It is not an abort
close the browser windowthe same thing happens

The terminal says so while it is recording, so there is nothing to remember:

  ● recording β€” drive the flow in the window
    press Enter here to stop and save
    Ctrl+C or closing the window save it too β€” nothing is lost

Only a second Ctrl+C, after the first one has been acknowledged, abandons the run β€” by then the file is usually already written.

Recording the browser you already have

The window record opens is a browser with a fresh profile: signed out of everything, no extensions, none of the state a demo of a real product is made of. --connect records the Chrome you are already using instead.

Start Chrome once with remote debugging on:

# macOS
open -na "Google Chrome" --args --remote-debugging-port=9222 --user-data-dir="$HOME/.narro-chrome"

# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.narro-chrome"

# Windows
chrome.exe --remote-debugging-port=9222 --user-data-dir="%TEMP%\narro-chrome"

Then record it:

npx @getnarro/cli record https://your.app --connect --html demo.html

--connect on its own means http://127.0.0.1:9222. A port, a host and port, or a full URL all work if you moved it: --connect 9333.

Two things worth knowing about that Chrome flag, because neither is guessable:

  • The separate --user-data-dir is not optional. Since Chrome 136 the default profile ignores --remote-debugging-port β€” a security fix β€” so a Chrome started without it accepts no connection and --connect reports that nothing is listening. The directory you name is a real profile that keeps its cookies: sign in once and every later recording is already signed in.
  • It has to be started with the flag. A Chrome that is already open cannot be given the port afterwards.

What attaching does, once connected:

  • If a tab is already showing the URL, that tab is recorded where it stands β€” no reload, so the half-filled form or the state you spent five minutes getting into is still there when recording starts. rrweb opens with a full snapshot of whatever is on screen, so a recording can begin in the middle of a session.
  • Otherwise a new tab opens on the URL. Your other tabs are not touched.
  • When the recording stops, narro disconnects. Your browser, and every tab in it, keeps running.
  • The recording viewport is whatever that window is, so --size does not apply. Resize the window before you start.

--connect and --auto are opposites β€” one records the browser you are driving, the other tours a page in a browser of its own β€” so the two cannot be combined.

The window it opens, and its size

--size is the size of the page, and the window is sized to match: a --size 1440x900 recording gets a window with 1440x900 of page in it, plus whatever height the tab strip and omnibox need on your platform.

npx @getnarro/cli record https://your.app --size 1280x800

If the screen cannot fit that β€” 1440x900 on a 1366x768 laptop wants a window with its foot below the display β€” the window is shrunk to fit and the terminal says what it recorded instead:

  1366x681
  (1440x900 would not fit on this screen)

The number printed is measured from the window rather than assumed, and it is what the cast records as its viewport. That matters more than it sounds: a cast is not responsive, and the replay scales the layout it captured, so a cast whose stated viewport was not the one the page laid itself out at replays at the wrong shape.

Two ways to embed it

They want different files, so ask for the one you need.

A website should serve the parts separately, because a site already knows how to serve and cache files:

npx @getnarro/cli record https://your.app --embed ./public/demo

That writes the player, the cast, and the assets as ordinary files, plus the snippet to paste:

<script src="/demo/narro-player.global.js"></script>
<narro-replay src="/demo/recording.narrocast" controls autoplay cursor="trail"></narro-replay>

The player is the same bytes for every demo on the site, so it caches once. The page itself pays for the event stream β€” tens of kilobytes for a short flow.

Beside those files it also writes EMBEDDING.md β€” the same demo written out for React, Astro, Preact, Vue and Svelte, with your own paths already in it β€” and PROMPT.md, which is that written as an instruction to hand a coding agent. record offers you the same prompt when it finishes, so the next step is one keystroke away rather than a file you have to go and find. Embedding a replay is the full version.

Anything you hand someone β€” an email, a deck that must work on a plane, a file on a stick β€” wants --html: one file, no server, no network.

On a slide

In a markdown deck, a fence β€” no import, no project:

```replay
src: /demo/recording.narrocast
poster: /demo.png
cursor: trail
```

Put the recording in the deck’s public/ directory, exactly as you would an image. Every option is listed under Replays.

In a React deck β€” or any React app β€” the typed wrapper lets the page drive the recording rather than the viewer, and keeps the 200 KB player out of the bundle until a replay is on screen:

import { Replay } from "@getnarro/replay/react";

<Replay src="/demo.narrocast" chapter={step === 2 ? "publish" : "open the editor"} />

Because narro export --pdf and --pptx produce files that cannot run JavaScript, record a still for them at the same time:

npx @getnarro/cli record https://your.app --html demo.html --poster demo.png

The cursor

A replay with no cursor is a page that changes for no visible reason β€” the viewer sees the result of a click and never the click. --cursor pointer draws one; --cursor trail also draws the path it took, which is what a flow through several screens usually needs; --cursor none hides it.

Unattended recordings (--auto 8) move the pointer as well as scrolling, for the same reason. They scroll whatever the page actually scrolls: an app shell β€” a full-height layout whose main pane moves while the document does not β€” is toured by its pane, so a tour of one is not a still frame with a cursor on it.

Selecting the text in a replay

A reader can drag across the words in a replay and highlight them, which is the one thing no video of an interface can offer, and worth pointing at in the caption. Three things follow from a live frame, and the player handles all three: a click cannot navigate away from the recording, the wheel scrolls the page around the demo rather than the demo, and a selection holds the replay where it is β€” text that scrolls out from under the pointer cannot be selected β€” until nothing is selected again. On a touch screen the frame stays inert, so a thumb pans the page.

What a cast is not

  • Not responsive. Layout was computed once, at the recording viewport, and every scroll offset in the stream assumes it. The player scales rather than reflows. Record a second cast at --size 390x844 for a phone.
  • Not the application. The recording carries the DOM your app produced, never its code β€” nothing runs on replay, so anything that was not recorded does not exist. Interactivity is a re-enactment, not a live product.
  • Blind to canvas and WebGL. A chart library, a map or a 3D viewer records as an empty element. Those demos still want a video.

Before you record something real

A cast is text, and grep works on it. It carries what was on screen and a great deal that was not: content behind display: none, options in a select nobody opened, everything below the fold, and β€” the part that surprises people β€” every attribute and URL, including title, alt, aria-label, data-* and any token sitting in an href.

--mask applies rrweb’s privacy profile, which masks input values and drops scripts and comments. It is worth using and it is not sufficient: it does not touch attributes or URLs at any setting. Recording a page of real customer data is a decision to publish that data, so record a demo account.

Why it is small

Measured on a real page, the portable single file is 414 KB where the obvious implementation produces 1.5 MB, and a website pays 60 KB for the cast:

  • the player carries rrweb’s replay half only β€” a page showing a recording has no reason to ship a recorder;
  • assets are stored once, under the hash of their bytes, rather than inlined wherever they appear, which otherwise ships the same font twice β€” and that includes images the page had already inlined as data: URIs, worth about a fifth of a cast on a page that repeats one;
  • images are re-encoded to WebP at the same dimensions;
  • and both halves travel gzipped, at the most zlib will give. Not brotli, which would be worth twice as much and which no browser will decompress from DecompressionStream β€” see how big it is.

Fonts are the one thing a cast must carry rather than link. A font file is fetched in CORS mode no matter whose stylesheet asked for it, and a self-hosted face ships no Access-Control-Allow-Origin header β€” because same-origin never needed one. On any other origin, every one of them is refused.

IntroductionWhat is Narro, and should I be writing markdown or React?
InstallationWhat do I install, and what does the project look like afterwards?
First DeckWhat does a working React deck look like, end to end?
Markdown ModeHow do I split a slide, style one word, or reveal a line β€” without leaving the markdown file?
Writing AI PromptsWhat do I tell an AI so the deck it writes actually builds?
Rules for AI AgentsWhat do I paste into my repo so an agent stops writing decks that build wrong?
React APIWhich component or hook do I import, and what does it take?
CLIWhich command do I run, and what are its flags?
Markdown APIHow do I read, edit, or validate a deck file from my own code instead of by hand?
Component ReferenceWhat props does this component take, and which package do I import it from?
Verifying a deckHow do I know my deck is actually correct?
AnimationHow do I reveal a list one line at a time, or move between slides with something other than a cut?
NavigationHow does the audience move through the deck, and how do I present it?
Canvas & PositioningHow do I put something at an exact position instead of in the flow?
Images & MediaHow do I use an image as a background, tint it, or embed a video?
ArchitectureWhich package owns what, and why is the seam where it is?
Zooming canvasHow do I zoom and pan across one big canvas instead of cutting between slides?
Transform ModeHow do I zoom and pan across one big canvas instead of cutting between slides?
Import & ExportHow do I get this deck out as PPTX, PDF, or one file I can email?
VideoHow do I turn a story into a video, and keep it right when the words, the product or the source changes?
Recording a web appHow do I put a demo of my product in a deck or on a landing page without shipping a video of it?
Embedding a replayHow do I embed a recorded demo of my app in my React app, my Astro site, or any other page?
TerminalHow do I put a terminal demo in a deck, a README or a video, and find out when the tool it demonstrates stops saying what the demo says?
AI IntegrationHow do I wire an AI assistant up to Narro so it can write and build decks?
ThemingHow do I change the colours, fonts, and overall look of a deck?
Deck TemplatesHow do I define one house style with named layouts my slides can reference, like a PowerPoint master?
TroubleshootingSomething is wrong with my deck. What is it, and how do I fix it?
LimitationsWhat can't Narro do, and what do I do instead?