Domotion

CLI reference

Every flag for `domotion capture` and `domotion animate`.

The domotion binary is installed when you npm install domotion-svg. It exposes two commands:

  • domotion capture <input> [options] — single-frame SVG.
  • domotion animate <config.json> — multi-frame animated SVG.

capture

domotion capture <input> [options]

<input> is one of:

  • A URL — https://example.com or http://localhost:3000/path.
  • A local HTML file path.
  • - to read HTML from stdin.
FlagDefaultEffect
-o, --output <path>stdout, or <input>.svg for filesWhere to write the SVG. Use -o - to stream to stdout explicitly.
--width <n>800Viewport width in CSS pixels.
--height <n>600Viewport height in CSS pixels.
--selector <css>"body"Element to capture; the SVG will be the size of this element's bounding box (clipped by --clip if present).
--clip x,y,w,hfull viewportCapture only this rectangle. Useful for cropping just one card or section out of a larger page.
--scroll-to x,y0,0Scroll the page to this offset before capturing — for capturing below-the-fold content.
--scroll <pattern>Generate an animated scroll demo from the page. Captures at multiple scroll positions and composes one animated SVG (DM-604). Examples: "down:bottom/8s", "720px,2s until bottom".
--scroll-speed <n>1500Default scroll speed in px/s for tokens in --scroll that don't specify /<duration>.
--scroll-selector <css>windowScroll a specific element instead of the window, for the --scroll animated-demo flow.
--no-prescrolloffSkip the pre-scroll-to-bottom-then-top step used to wake lazy-loaded content before the --scroll demo records.
--wait <ms>200Sleep this long after the page settles. Bump up if you have lazy-loaded images or CSS transitions in flight.
--wait-for <css>Wait for the matching element to become visible before capturing. Most reliable for SPA-driven content.
--no-fonts-readyoffSkip the document.fonts.ready wait. By default Domotion waits for web fonts.
--optimizeoffRun output through SVGO. 30–50% smaller; reduces decimal precision so diffs get noisier.
--warningsoffLog capture warnings to stderr after the run (CSS features that didn't round-trip).
--mobileoffEmulate a mobile device (iOS user-agent, isMobile=true). Sites that branch on UA or use @media (hover: none) render their mobile path.
--color-scheme <s>Sets prefers-color-scheme: light, dark, or no-preference. Pages that use @media (prefers-color-scheme: dark) render their dark variant.

capture examples

# Default: capture the whole body of example.com at 800×600.
domotion capture https://example.com -o example.svg

# Capture only the .pricing-table at retina-friendly 1280×720,
# wait for it to load, optimize.
domotion capture https://yoursite.com/pricing \
  --width 1280 --height 720 \
  --selector ".pricing-table" \
  --wait-for ".pricing-table .price" \
  --optimize \
  -o pricing.svg

# Capture a local HTML file, only the bottom-right 400×200 region.
domotion capture ./demo.html --clip 200,200,400,200 -o slice.svg

# Capture HTML produced by a build step, no temp file.
node build-demo-html.js | domotion capture - --width 600 --height 200 -o out.svg

# Mobile capture: iOS user-agent + isMobile.
domotion capture https://example.com \
  --width 390 --height 844 --mobile -o phone.svg

# Capture a dark-mode-aware page in dark mode.
domotion capture https://example.com --color-scheme dark -o dark.svg

animate

domotion animate <config.json> [-o path] [--optimize]

<config.json> describes one or more frames; each frame is captured (optionally after running an interaction) and the result is stitched into a single animated SVG with CSS keyframe transitions.

Config schema

{
  "width":  800,
  "height": 400,
  "output": "demo.svg",        // optional, overridden by -o
  "optimize": true,             // optional, --optimize on the CLI also works
  "chrome": { "type": "browser", "url": "https://yoursite.com" },  // optional
  "frames": [
    {
      "input":      "./frames/start.html",    // or a URL
      "duration":   1500,                     // ms held on screen
      "transition": { "type": "crossfade", "duration": 300 },
      "selector":   "body",                  // optional capture root
      "wait":       200,                     // optional ms after settle
      "waitFor":    ".ready",                // optional CSS selector
      "scroll":     [0, 0],                  // optional [x, y]
      "actions":    [...],                    // see "Actions" below
      "overlays":   [...]                     // see "Overlays" below
    }
  ]
}

Paths in "input" resolve relative to the config file's directory. Each frame opens its input in the same Playwright context, runs its actions (if any), waits the requested time, and is captured.

Transitions

TypeWhat it does
"crossfade"Old frame fades out as new frame fades in. Best for shared layouts where individual elements are crossing over.
"cut"Instant — the next frame replaces the current one with no fade or slide. duration is ignored. Best for "this just changed" beats: a progress bar resizing, a new line appearing in a terminal, a panel toggling.
"push-left"New frame slides in from the right, old frame slides out to the left. Best when the two frames are unrelated screens.
"scroll"Vertical scroll between frames; treats them as a single tall canvas. Best for "scroll through the page" demos.

Actions

Actions run in order before the frame is captured. Each is a simple object:

{ "type": "click",  "selector": ".cta" }
{ "type": "fill",   "selector": "input[name=q]", "value": "hello" }
{ "type": "press",  "key": "Enter" }
{ "type": "hover",  "selector": ".tooltip-trigger" }
{ "type": "scroll", "y": 200 }
{ "type": "wait",   "ms": 300 }

Use wait to give animations / async UI a chance to settle between actions.

Animations (intra-frame)

Animations declared on a frame run during its hold time. Use them for property changes that happen within a single frame: progress bar filling, content scrolling, an element fading in. Each animation resolves its selector against the captured DOM and tags matching elements with a CSS class the renderer targets via keyframes.

{ "selector": ".bar", "property": "transform",
  "from": "scaleX(0)", "to": "scaleX(1)",
  "duration": 2000, "easing": "ease-out", "delay": 150 }

Supported property values: transform, opacity, width, height, translateX, translateY. Use full transform strings (scaleX(0.5), rotate(15deg)) for the transform property. The translateX / translateY shorthand expects raw values ("240px", "-50%").

For "fill from left" effects (progress bars), use transform: scaleX(0)→scaleX(1) with transform-origin: left set on the source element. CSS width animations on SVG-rendered rects don't compose cleanly because of how SVG percentage units work — scaleX on a wrapper group is the reliable path.

Overlays

Overlays draw on top of the captured frame so you can simulate typing or taps without modifying the page. Two kinds:

{ "kind": "typing", "text": "hello", "x": 20, "y": 40, "speed": 80 }
{ "kind": "tap",    "x": 120, "y": 90, "delay": 600 }
{ "kind": "svg",    "src": "./preview.svg", "x": 180, "y": 0,
                  "width": 280, "height": 580,
                  "enter": { "from": "bottom", "duration": 600, "easing": "ease-out" } }

The svg overlay inlines a separately-captured SVG file as a picture-in-picture layer. src is resolved relative to the config file's directory; ids in the embedded SVG are namespaced automatically. Use enter / exit for slide-in sugar — from is one of "top", "bottom", "left", "right".

See Overlay types for the full property list.

Exit codes

CodeMeaning
0Capture wrote successfully.
1Runtime error (browser launch failed, page navigation failed, etc).
2Argument / config error.

When to drop down to the API

  • You want a custom interaction loop the JSON action vocabulary can't express (e.g. drag-and-drop, multiple-step flows where the next action depends on a captured value).
  • You want to compose frames programmatically — e.g. one frame per row of a database query.
  • You're integrating capture into an existing test harness (Playwright, Vitest) and want to share the browser context.

Otherwise, the CLI covers the common cases. See API overview when you outgrow it.