Skip to content

tool3/dvd

Repository files navigation

dvdrw — animated SVG terminal recordings, programmatic

Generate animated SVG terminal recordings from code.

npm version npm downloads license cli

dvdrw is the Node library behind dvd-cli — call it from your own code, your build pipeline, or your service. Output is a single self-contained animated SVG. No ffmpeg, no headless browser, no shelling out.

npm install dvdrw
import dvd from 'dvdrw';

const { svg } = await dvd(`
  Type "echo hello world"
  Enter
  Sleep 800ms
`, { theme: 'dracula', template: 'macos' });

quick start


Contents


Why the library?

The CLI is great when you have a .cd script in a file. The library is for everything else:

  • Programmatic content — render the output of a real test run, a real deploy, or a real benchmark, with frames built from runtime data.
  • Raw stdout capture — feed any ANSI byte stream straight in ({ raw }). Spinners, progress bars, chartscii, lolcat, anything that animates on the terminal.
  • Build-pipeline integration — fully async, optional onProgress callback, no temp files, no subprocesses by default.
  • Embeddable — drop into a docs generator, a service, an Electron app, a serverless function. The output is a string.
  • Composable — the parser, terminal emulator, coalescer, emitter and animator are all exported and usable independently.

If you want a single command on the CLI that takes a .cd file and writes a .svg, use dvd-cli. If you want to call into the engine, you're in the right place.


Inputs

dvd(input, options) accepts four input shapes. Pick whichever matches the data you already have.

1. CD script string

The fastest path. Same syntax as dvd-cli, just inlined.

const { svg } = await dvd(`
  Type "npm install dvdrw"
  Sleep 400ms
  Enter
  Sleep 800ms
`, { theme: 'dracula', template: 'macos', title: 'quick-start' });

2. Programmatic steps

When the content of your animation is computed at runtime — a generated test report, a stream of deploy events, a templated demo — skip the script and pass an array.

// Each Type+Enter is sent through a real shell, so wrap any styled output
// in `echo -e "..."` rather than typing raw ANSI as a command.
const steps = [
  { type: 'Type', text: 'echo -e "\\x1b[2m$\\x1b[0m npm test"' },
  { type: 'Key', key: 'Enter' },
  { type: 'Sleep', duration: 600 },
  ...tests.flatMap((t) => [
    { type: 'Type', text: `echo -e "\\x1b[32m  ✓\\x1b[0m ${t.name} \\x1b[2m(${t.ms}ms)\\x1b[0m"` },
    { type: 'Key', key: 'Enter' },
    { type: 'Sleep', duration: 200 },
  ]),
];

const { svg } = await dvd(steps, { theme: 'tokyoNight', template: 'macos', title: 'test runner' });

programmatic test runner output

Full source: examples/02-programmatic-steps.ts

3. Raw terminal output

Capture stdout from any command and hand the bytes over. dvd auto-detects the animation pattern (cursor reset, terminal reset, clear-line, cursor-up) and splits into frames automatically.

import dvd from 'dvdrw';
import { spawnSync } from 'node:child_process';

const r = spawnSync('myscript.sh', { encoding: 'buffer' });
const raw = r.stdout.toString('binary');

const { svg } = await dvd({ raw, totalDuration: 2400 }, {
  theme: 'catppuccinMocha',
  template: 'macos',
  title: 'spinner capture',
});

captured spinner output

Full source: examples/03-raw-output.ts

4. Pre-parsed script

If you've already parsed a CD script (e.g., for validation or transformation), pass the AST directly:

import dvd, { parseCDScript } from 'dvdrw';

const script = parseCDScript(scriptText);
// ...mutate, validate, splice frames...
const { svg } = await dvd({ script }, { theme: 'nord' });

Themes

37 built-in themes, all exported from themes. Pass by name or as a full Theme object.

await dvd(script, { theme: 'tokyoNight' });
dracula
dracula
tokyoNight
tokyoNight
catppuccinMocha
catppuccinMocha
nord
nord
gruvboxDark
gruvboxDark
monokai
monokai
oneDark
oneDark
synthwave84
synthwave84

The full list: a11yDark, base16Dark, base16Light, blackboard, catppuccinMocha, cobalt, dark, dracula, draculaPro, duotoneDark, githubDark, githubLight, gruvboxDark, gruvboxLight, hopscotch, lucario, material, monokai, night3024, nord, oceanicNext, oneDark, oneLight, pandaSyntax, paraisoDark, seti, shadesOfPurple, solarizedDark, solarizedLight, synthwave84, terminal, tokyoNight, twilight, verminal, vscode, yeti, zenburn.

For custom palettes, pass a Theme object directly:

import dvd, { type Theme } from 'dvdrw';

const retroGreen: Theme = {
  name: 'retro',
  background: '#0a0a0a',
  foreground: '#00ff00',
  cursor: '#00ff00',
  // ...the 16 ANSI colors
};

await dvd(script, { theme: retroGreen });

Source: examples/04-themes-gallery.ts


Templates

Window chrome — macos / windows / minimal.

macos windows minimal
macos template windows template minimal template

Source: examples/09-templates.ts


Loop styles

Animations loop by default. Choose how the loop behaves at the boundary:

await dvd(script, {
  loopStyle: 'reverse',  // 'loop' | 'reverse' | 'rewind' | 'fade'
  loopPause: 600,
  rewindSpeed: 6,        // for 'rewind'
  fadeDuration: 1200,    // for 'fade'
});
loop — restart from frame 0
loop style: loop
reverse — play forward then back
loop style: reverse
rewind — fast reverse like rewinding tape
loop style: rewind
fade — fade to black, fade back in
loop style: fade

Source: examples/05-loop-styles.ts


Branded output

Gradient backgrounds, watermarks, custom borders — for docs sites and landing pages where the SVG carries product weight.

const { svg } = await dvd(script, {
  theme: 'tokyoNight',
  template: 'macos',
  background: 'gradient(#7c5fff, #ff6ec7:diagonal)',
  backgroundPadding: 48,
  borderRadius: 12,
  watermark: 'made with dvd',
});

Backgrounds accept solid colors (#1a1a2e) or gradients in the form gradient(<color>, <color>[:vertical|horizontal|diagonal]) with as many stops as you need.

branded output

Source: examples/06-branding.ts


Progress tracking

Wire dvd into your build pipeline or TUI. The onProgress callback fires for every step the executor runs.

await dvd(script, {
  onProgress: (current, total, description) => {
    const pct = Math.round((current / total) * 100);
    process.stdout.write(`\r[${pct}%] ${description ?? ''}`);
  },
});

The returned result also carries metadata — frame count, total duration, effective FPS — for logging and CI annotations:

const result = await dvd(script);
console.log(result.metadata); // { duration: 2118, frameCount: 33, fps: 15.6 }

Source: examples/07-progress.ts


Low-level API

Skip the executor when you want to render arbitrary terminal state directly — for static badges, dashboards, CI annotations, or content that doesn't fit the script model.

import { coalesce, createGridState, emit, processInput, themes } from 'dvdrw';

const fontSize = 16;
const theme = themes.draculaPro;

let state = createGridState(44, 9);
state = processInput(state, '\x1b[1;38;5;213m  dvdrw\x1b[0m · terminal recordings as svg');

const spans = coalesce(state, theme);

const { svg } = emit(spans, state.cursor, false, {
  theme,
  template: 'minimal',
  width: 460,
  height: 240,
  fontSize,
  lineHeight: fontSize * 1.4,
  charWidth: fontSize * 0.6,
  padding: 16,
});

low-level API output

The exposed building blocks:

Module Symbols
Terminal emulator createGridState, processInput, applyCommand, applyCommands, parseInput
Text processing coalesce
Emission emit, emitAnimated, emitFilmstripAnimated
Animation createAnimatedSVG, createFilmstripSVG, optimizeSvg
Raw output processRawOutput, detectAnimationType, splitIntoFrames
Cast files parseCastFile, RecordingPlayer, generateFramesFromRecording
Script parsing parseCDScript, CDParseError
Executor CDExecutor

Source: examples/08-low-level-api.ts


Rendering modes: filmstrip vs SMIL

Two animation engines are available. The default (filmstrip) is what you almost always want.

Filmstrip (default) SMIL (smil: true)
Engine CSS @keyframes over a deduped row pool Native SVG <animate> per frame
File size Smaller — scales with unique rows, not total frames Larger — scales with total frames
Best for README embeds, docs, long recordings, mostly-static content Short animations, high-FPS smoothness on iOS Safari / 120Hz
await dvd(script, { smil: true });

Options reference

const { svg, frames, frameData, metadata } = await dvd(input, {
  // Window chrome
  theme,                  // Theme name (string) or Theme object — default 'dark'
  template,               // 'macos' | 'windows' | 'minimal' — default 'macos'
  title,                  // window title text
  watermark,              // string or SVG markup

  // Dimensions
  width, height,          // omit for auto-sizing from script content
  fontSize,               // default 14
  lineHeight,             // multiplier — default 1.4
  letterSpacing,
  fontFamily,
  padding,                // default 16

  // Borders
  borderRadius,           // default 8
  borderColor, borderWidth,

  // Background (outside the terminal window)
  background,             // '#hex' or 'gradient(#a, #b[:horizontal|vertical|diagonal])'
  backgroundPadding,
  backgroundRadius,

  // Header / footer
  headerHeight, headerBackground, headerBorder,
  headerBorderColor, headerBorderWidth,
  footerHeight, footerBackground, footerBorder,
  footerBorderColor, footerBorderWidth,

  // Cursor
  cursorStyle,            // 'block' | 'bar' | 'underline'
  cursorColor,
  cursorBlink,            // default false

  // Animation
  fps,
  loop,                   // default true
  loopStyle,              // 'loop' | 'reverse' | 'rewind' | 'fade'
  loopPause,              // ms between cycles
  pauseAtEnd,             // ms hold on last frame — default 1000
  fadeDuration,           // ms for 'fade' style — default 1500
  rewindSpeed,            // multiplier for 'rewind' — default 5
  playbackSpeed,          // 1 = normal, 2 = 2x, 0.5 = half speed

  // Renderer
  smil,                   // false = filmstrip (default), true = SMIL
  optimize,               // SVGO post-pass — default true
  customGlyphs,           // box-drawing as geometric shapes — default true

  // Callbacks
  onFrame,                // (frame: TerminalFrame) => void
  onProgress,             // (current, total, description?) => void
});

The result:

result.svg                  // animated SVG string
result.metadata.duration    // total ms
result.metadata.frameCount  // number of frames
result.metadata.fps         // effective fps
result.frames               // TerminalFrame[]
result.frameData            // FrameData[] — raw row data, useful for custom emitters

Steps reference

When using the programmatic-steps input, each entry conforms to one of these shapes:

Type Fields Example
Type text, optional speed (ms/char) { type: 'Type', text: 'hello', speed: 50 }
Key key { type: 'Key', key: 'Enter' }
Sleep duration (ms) { type: 'Sleep', duration: 1000 }
Shortcut key + modifier flags { type: 'Shortcut', ctrl: true, key: 'c' }
Screenshot path { type: 'Screenshot', path: 'frame.svg' }
Copy / Paste text (Copy only) { type: 'Copy', text: 'hi' }
Set setting, value { type: 'Set', setting: 'Theme', value: 'dracula' }
Env key, value { type: 'Env', key: 'NODE_ENV', value: 'prod' }

Keys: Enter, Backspace, Tab, Space, Left, Right, Up, Down.


Comparison

dvdrw VHS asciinema
Output SVG GIF / MP4 asciicast
Native API TypeScript lib CLI JSON + player
Dependencies none ffmpeg, ttyd player embed
Scalable yes no yes
GitHub README perfect works embed only
Editable yes (XML) no yes (JSON)
Offline yes yes no
Loop styles 4 modes basic basic
Programmatic yes limited yes

Examples

All runnable. Each writes its SVG into examples/svgs/.

npx ts-node -P tsconfig.dev.json examples/01-quick-start.ts
npx ts-node -P tsconfig.dev.json examples/02-programmatic-steps.ts
npx ts-node -P tsconfig.dev.json examples/03-raw-output.ts
npx ts-node -P tsconfig.dev.json examples/04-themes-gallery.ts
npx ts-node -P tsconfig.dev.json examples/05-loop-styles.ts
npx ts-node -P tsconfig.dev.json examples/06-branding.ts
npx ts-node -P tsconfig.dev.json examples/07-progress.ts
npx ts-node -P tsconfig.dev.json examples/08-low-level-api.ts
npx ts-node -P tsconfig.dev.json examples/09-templates.ts

# or render them all in one go:
npx ts-node -P tsconfig.dev.json examples/10-render-all.ts

Related

  • dvd-cli — the CLI front-end (.cd scripts, pipe mode, rec / render sub-commands)
  • shellfie — terminal screenshots in code
  • shellfie-cli — terminal screenshots CLI
  • shellfied — terminal screenshots web service

License

MIT

About

Create simple animated SVGs from your terminal

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors