Skip to content

Repository files navigation

DVD — animated SVG terminal recordings

DVD

Animated SVG terminal recordings.
Write a script, run dvd, get an infinitely-scalable animation you can drop in any README.

npm versionnpm downloadslicensenode >=18

Quick start · Why SVG · Recording · Syntax · Themes · CLI · FAQ


What you get

DVD turns terminal output into a single self-contained animated SVG. No ffmpeg. No headless browser. No video encoder.

Because the output is SVG, it is text: the frames are real glyphs, so the result stays sharp at any zoom, weighs almost nothing on the wire, and can be edited, diffed, and version-controlled like source.

npx dvdrw-cli demo.cd

hello world demo


Quick start

Install

Homebrew
brew install tool3/tap/dvd
Shell
curl -fsSL https://raw.githubusercontent.com/tool3/dvd-cli/master/scripts/install.sh | bash
npm
npm install -g dvdrw-cli # global
npx dvdrw-cli --help # no install
npm install -D dvdrw-cli # per-project

The npm package is dvdrw-cli; the binary it installs is dvd. With npx you must use the full package name: npx dvdrw-cli.

Your first recording

dvd new demo

That scaffolds demo.cd:

Output demo.svg
Set Template macos
Set Theme dracula
Set Title "My Terminal"
Type "echo 'Hello World!'"
Sleep 500ms
Enter
Sleep 1s

Render it:

dvd demo.cd

Then embed it anywhere that takes an image:

![Demo](demo.svg)

Why SVG

An honest comparison — including where DVD is the wrong tool.

DVDVHSasciinema
Outputanimated SVG + MP4/WebM/GIFGIF / MP4 / WebM.cast + player
External binaries to rendernone for SVG, ffmpeg for videoffmpeg, ttydnone
Resolution-independentyes (SVG)noyes
Text is real textyes (SVG)noyes
Works as a plain <img>yesyesno (embed)
Single self-contained fileyesyesno (needs player/host)
Loop styles4 modesbasicbasic
Print / retina qualityyes (SVG)nono
Video for social & slidesyesyesno
Hosted sharing & playbacknonoyes (asciinema.org)

Use DVD for README embeds, docs sites, and anywhere you want a crisp, tiny, text-based animation — and reach for -o demo.mp4 when you need a video of the same recording.

Use asciinema when you want hosted, shareable, pausable playback with a real player.

About file size

Raw SVG output looks large, but it is highly repetitive text and every HTTP server gzips it. Measured from examples/:

FileRawGzipped
intro_original.svg2.4 MB39 KB
rainbow.svg272 KB8 KB
chartscii-stdin.svg3.7 MB304 KB

The wire cost is small. The real cost of a very long recording is DOM size and browser memory, not bandwidth — see FAQ.


Video output

SVG does not embed on social platforms, and some places want a real video. Give --output a video extension and you get one:

dvd demo.cd -o demo.mp4 # H.264, yuv420p — plays everywhere
dvd demo.cd -o demo.webm # VP9
dvd demo.cd -o demo.gif # palette-optimised GIF

Works the same for the other two input modes:

chartscii 3 5 8 -e | dvd -o chart.mp4
dvd render session.cast -o session.mp4

What video needs

Video is entirely opt-in — installing dvd pulls in nothing extra, and SVG output needs neither of these:

brew install ffmpeg # or apt / winget. Set FFMPEG_PATH to override.
npm install -g @resvg/resvg-js # optional peer dep, ~3MB prebuilt, no compiler

If either is missing, dvd tells you which one and how to get it.

Why a second tool at all: the animated SVG is SMIL, and nothing outside a browser executes SMIL — so DVD does not convert the SVG into video. It re-renders each frame of the recording as a still image and streams those through ffmpeg, which means the video comes from the same frame data as the SVG rather than being a lossy copy of it. Turning those frames into pixels needs a rasterizer, and Node has no built-in one (ffmpeg only decodes SVG if it was compiled against librsvg, which most builds are not).

FlagMeaning
--quality, -qlow, medium (default) or high
--fpsOutput frame rate — overrides the tier's own (see below)
--loopsTimes the animation repeats (default 1)
--font-fileFont to rasterize with — otherwise a system monospace is used

Quality

dvd demo.cd -o demo.mp4 -q high
TierScaleFPSLooks like
low1x15Fine in a chat window; visible softness on edges
medium1x30Pixel-for-pixel with the SVG at 100%
high3x30 / 60Indistinguishable from the SVG, including zoomed

high picks its frame rate from the recording: it measures the tightest gap between frames and rounds up, so a slow typing demo stays at 30 while a fast in-place animation (chartscii -e, a spinner) gets 60. Sampling faster than the source ever changes would only duplicate frames. --fps overrides all of this.

The lever that matters is supersampling, not bitrate. Terminal output is thin, high-contrast glyph edges — the worst case for a block-based codec at 1:1. high renders the vector at triple size (real extra detail, not an upscaled bitmap) and lets the player downscale, which is what makes text read as crisply as the SVG at any zoom.

Flat colour compresses almost for free, so the cost is far smaller than the pixel count suggests. A representative clip:

TierOutputFPSSize
low700x240154.0KB
medium700x240306.3KB
high2100x7206020.8KB

Encode time grows with pixels and frames, so high is roughly 9x the pixel work of medium at up to twice the frame count.

Two things behave differently from the SVG, both unavoidable:

  • Cursor blink is off. Blink is a CSS animation; a still frame can only sample one phase of it.
  • Dimensions round up to even numbers. H.264's yuv420p subsampling rejects odd width or height.

Frames the animation holds still — the pauses between keystrokes, Sleep commands — are rasterized once and reused, so encoding time tracks the number of visible changes, not the frame count.


Three ways to record

1. Scripted — .cd files

Declarative and reproducible. Best for docs you want to regenerate in CI.

dvd demo.cd
dvd demo.cd -o out.svg --theme nord --template minimal

2. Piped — any command's output

Wrap a command and capture exactly what it printed, colors and all.

ls -la --color | dvd -o listing.svg
neofetch | dvd -o system-info.svg --title "System Info"
lolcat -a -d 2 <<<"Hello World"| dvd -o rainbow.svg

neofetch piped into dvd

Animated command output is captured frame by frame:

chartscii $(seq 1 5) -c "gradient(pink,cyan)" --animate | dvd -L reverse -P 1000 -w "made with dvd"

animated chartscii chart

3. Live — record a real session

dvd rec captures an interactive PTY session to an asciinema v2 .cast file. Exit the shell or press Ctrl+D to stop.

dvd rec # -> recording.cast
dvd rec session.cast # custom path
dvd rec session.cast --command "ls -la --color"# one-shot, non-interactive
dvd rec session.cast --title "Demo session"# embed a title

The output is standard asciinema v2, so it works with any asciinema-compatible tool.

Already have .cast files? DVD renders them straight to SVG — dimensions are auto-derived from the recording:

dvd render recording.cast
dvd render recording.cast -o demo.svg --theme dracula --template macos --title "Demo"
dvd render recording.cast --no-cursor --font-size 16 --loop-style reverse
Render options for .cast files
OptionAliasDescriptionDefault
--output-oOutput SVG path<file>.svg
--theme-TColor themedark
--template-mmacos, windows, minimalmacos
--title-tWindow title
--font-size-sFont size (px)14
--line-height-YLine height multiplier1.4
--padding-dContent padding (px)16
--border-radius-RWindow border radius (px)8
--cursor-blinkEnable cursor blinkfalse
--no-cursorHide cursor entirely
--custom-glyphs-GRender block elements as shapestrue
--loop-style-Lloop, reverse, rewind, fadeloop
--optimize-OOptimize SVG outputtrue
--verbose-vVerbose outputfalse

Showcase

ANSI & truecolor
ansi colors
ASCII art
figlet ascii art
Charts
chartscii
Animated output
rainbow lolcat
Git log
git log
Directory listing
ls with colors
Text selection
text selection
Color tables
256 color table

More in examples/.


Syntax reference

.cd scripts are declarative. Lines starting with # are comments.

Commands

CommandPurposeExample
TypeType text with realistic timingType "echo hi"
EnterExecute the current commandEnter
SleepPause the recordingSleep 500ms / Sleep 2s
BackspaceDelete charactersBackspace 4
Left/RightMove the cursorLeft 5
ScreenshotCapture a static frameScreenshot test-results.svg

Typing speed can be set per-command with an @<ms>ms suffix:

Type@100ms "Slow typing..."
Type@10ms "Speed typing!"

Editing works as you'd expect:

Type "Hello Wrold"
Backspace 4
Type "orld!"

backspace demo

Keyboard navigation and selection are fully supported:

Shift+Left # Select character left
Shift+Right # Select character right
Alt+Left # Move word left
Alt+Right # Move word right
Alt+Shift+Left # Select word left
Alt+Shift+Right # Select word right
Cmd+Left # Move to line start
Cmd+Right # Move to line end
Cmd+Backspace # Delete word

keyboard navigation

Settings

Every setting uses Set <Setting> <value>. Output is the one bare directive.

Output demo.svg
Output path/to/output.svg
Appearance
Set Theme dracula # see the Themes gallery below
Set Template macos # macos | windows | minimal
Set Title "My Terminal"
Set Padding 16
Set BorderRadius 8
Set BorderWidth 2
Set BorderColor #ff0000
border
Dimensions — omit for auto-sizing
Set Width 800
Set Height 600
Fonts
# System font — the viewer must have it installed
Set FontFamily "Fira Code"
Set FontSize 14
Set LineHeight 1.4
# Embedded font — guaranteed to render identically everywhere
Set EmbedFont path/to/font.woff2

Use Set EmbedFont for anything public. A system font that the viewer lacks will silently fall back.

embedded font
font size 10
font size 20
font size 40

Cursor
Set CursorStyle block # block | bar | underline
Set CursorColor #ffffff
Set CursorBlink true
cursor styles
Prompt & typing speed
Set TypingSpeed 50 # default ms per character
Set PromptPrefix "$ "
Set PromptPrefix "❯ "
Set PromptPrefix "\x1b[95m❯\x1b[0m " # ANSI escapes work
custom prompt
Header & footer
Set HeaderHeight 40
Set HeaderBackground #333333
Set HeaderBorder true
Set HeaderBorderColor #444444
Set HeaderBorderWidth 1
Set FooterHeight 30
Set FooterBackground #333333
Set FooterBorder true
header and footer
Watermark
Set Watermark "Made with DVD"
Set WatermarkStyle "opacity: 0.5; padding: 10"

Raw SVG markup is allowed, so a watermark can be a link:

Set Watermark `<a href="https://github.com/tool3/dvd-cli">
<text text-anchor="end">DVD</text>
</a>`
Backgrounds & gradients
# Solid
Set Background #1a1a2e
Set BackgroundPadding 40
# Vertical gradient (default direction)
Set Background gradient(#667eea, #764ba2)
# Horizontal
Set Background gradient(#f093fb, #f5576c:horizontal)
# Multi-stop
Set Background gradient(#ff6b6b, #feca57, #48dbfb, #ff9ff3)
Set BackgroundPadding 60

solid background
vertical gradient
gradient background
horizontal gradient

Execution environment
Set Shell /bin/zsh
Set WorkingDirectory $PWD # or an absolute path
Playback speed
Set PlaybackSpeed 2 # 2x faster
Set PlaybackSpeed 0.5 # half speed

half speed
1x speed
2x speed

Full grammar: FORMAT.md.


Loop styles

Four ways to handle the end of a recording — a small thing that makes README animations feel deliberate rather than jarring.

StyleBehaviourTuning
loopRestart from the beginning (default)Set LoopPause 2000
reversePlay forward, then backward at the same speedSet LoopPause 2000
rewindFast reverse, like rewinding a tapeSet RewindSpeed 10 (def. 5)
fadeFade to black before restartingSet FadeDuration 1500
Set LoopStyle reverse

reverse loop
rewind loop
fade loop


Themes

37 built-in themes. dvd themes lists them all.

Set Theme <theme-name>
a11yDark
a11yDark
base16Dark
base16Dark
base16Light
base16Light
blackboard
blackboard
catppuccinMocha
catppuccinMocha
cobalt
cobalt
dark
dark
dracula
dracula
draculaPro
draculaPro
duotoneDark
duotoneDark
githubDark
githubDark
githubLight
githubLight
gruvboxDark
gruvboxDark
gruvboxLight
gruvboxLight
hopscotch
hopscotch
lucario
lucario
material
material
monokai
monokai
night3024
night3024
nord
nord
oceanicNext
oceanicNext
oneDark
oneDark
oneLight
oneLight
pandaSyntax
pandaSyntax
paraisoDark
paraisoDark
seti
seti
shadesOfPurple
shadesOfPurple
solarizedDark
solarizedDark
solarizedLight
solarizedLight
synthwave84
synthwave84
terminal
terminal
tokyoNight
tokyoNight
twilight
twilight
verminal
verminal
vscode
vscode
yeti
yeti
zenburn
zenburn

Custom themes

Pass a JSON object. Unspecified colors inherit from the current theme, so partial overrides are fine.

Set Theme {"background": "#1a1a2e", "foreground": "#eaeaea", "cursor": "#f39c12", "red": "#e74c3c", "green": "#2ecc71", "blue": "#3498db"}
Set Theme {"background": "#0d1117", "foreground": "#c9d1d9"}
Set Theme {"background": "#0a0a0a", "foreground": "#00ff00", "cursor": "#00ff00"}

custom theme
partial theme override

All theme properties
PropertyDescription
nameTheme name (optional)
backgroundTerminal background color
foregroundDefault text color
cursorCursor color
selectionSelection highlight color
black, red, green, yellow, blue, magenta, cyan, whiteStandard ANSI colors
brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhiteBright ANSI colors

Templates

macos
traffic lights
windows
Windows buttons
minimal
no chrome
macos templatewindows templateminimal template

Animation engines

DVD ships two engines. Filmstrip is the default and is right for almost everyone. SMIL is a targeted fix for one specific problem.

Filmstrip (default)

Each unique row is emitted once as an SVG <symbol> and referenced from every frame that uses it via <use>. Frame cadence comes from CSS @keyframes with step-end visibility switching.

  • Smaller files. Size scales with the number of unique rows, not frames. Repetitive output — prompts, ASCII art, mostly-static screens — compresses dramatically.
  • Well-optimized on desktop browsers, which is where README and docs traffic lands.
  • Can stutter at 120Hz on mobile Safari. Every frame switch goes through the browser's CSS style-resolution pipeline, which adds per-tick overhead on some devices.

SMIL (--smil)

Each frame is its own <g> group, and visibility is switched by a native SVG <animate attributeName="visibility">. The SVG engine pre-computes the schedule and paints only the active frame.

  • Smoother on 120Hz, mobile Safari, and iOS Chrome — the native path skips CSS style resolution entirely.
  • Larger files. Size scales with total frame count rather than unique rows: typically 2–4× filmstrip, more for long or highly-varied recordings.
  • SMIL is less actively maintained in browser specs than CSS animations, so treat it as a tool for a known problem rather than a default.

Which one?

You want…Use
A README or docs embed on desktopFilmstrip
The smallest possible fileFilmstrip
Long recordings with lots of repeated prompt linesFilmstrip
Buttery-smooth playback on iOS / 120Hz screensSMIL
Short, high-FPS animations where smoothness mattersSMIL

CLI reference

dvd script.cd # render to script.svg
dvd script.cd -o output.svg # custom output
dvd script.cd --verbose # detailed output
dvd new my-demo # scaffold a script
dvd new my-demo --template showcase # scaffold from a template
dvd themes # list all themes
dvd validate script.cd # check syntax without rendering
dvd rec session.cast # record a live session
dvd render session.cast -T dracula # render a .cast to SVGcommand| dvd -o output.svg # pipe mode
All options
OptionAliasDescriptionDefault
--output-oOutput file path<input>.svg
--verbose-vShow detailed outputfalse
--optimize-OOptimize SVG outputtrue
--smilUse the SMIL enginefalse
--loop-lLoop the animationtrue
--loop-style-Lloop, reverse, rewind, fadeloop
--loop-pause-PPause before loop restarts (ms)0
--pause-at-end-pPause at end before looping (ms)1000
--fade-duration-FFade duration for fade style (ms)1500
--rewind-speed-rSpeed multiplier for rewind5
--fps-fFrames per second
--playback-speed-SPlayback speed multiplier1
--theme-TColor themedark
--template-mmacos, windows, minimalmacos
--title-tWindow title
--width-WWidth in pixelsauto
--height-HHeight in pixelsauto
--font-size-sFont size in pixels14
--font-family-yFont family name
--line-height-YLine height multiplier1.4
--letter-spacing-aLetter spacing in pixels0
--padding-dContent padding (px)16
--border-radius-RBorder radius (px)8
--border-color-CBorder color (hex)
--border-width-BBorder width (px)
--background-AOuter background color or gradient
--background-padding-nPadding around the window (px)0
--cursor-style-cblock, bar, underlineblock
--cursor-color-kCursor color (hex)
--cursor-blink-KEnable cursor blinktrue
--custom-glyphs-GBlock elements as geometric shapestrue
--header-background-bHeader background color (hex)
--header-height-eHeader height in pixels
--header-border-DShow header border
--header-border-color-EHeader border color (hex)
--footer-background-gFooter background color (hex)
--footer-height-iFooter height in pixels
--footer-border-IShow footer border
--footer-border-color-JFooter border color (hex)
--footer-border-width-jFooter border width (px)
--watermark-wWatermark text

FAQ

Do animated SVGs actually work in GitHub READMEs?

Yes. CSS and SMIL animations run when an SVG is loaded as an image. Two things to know:

  1. Use raw.githubusercontent.com URLs, not github.com/.../blob/... — blob URLs serve an HTML page, so the image will appear broken.
  2. GitHub proxies and caches images through camo. If you update an SVG in place and the old one still shows, append a cache-buster: demo.svg?v=2.

Links inside an SVG are not clickable when it is embedded as an image.

My output is several megabytes. Is that a problem?

Usually not — SVG is repetitive text and gzips 10–60×, so a 2.4 MB file is ~39 KB on the wire. What does matter is DOM size in the browser for very long recordings.

If a recording feels heavy: keep it under ~30 seconds, raise Set TypingSpeed so fewer frames are generated, lower --fps, and stay on the default filmstrip engine.

The font looks wrong on someone else's machine.

Set FontFamily references a font by name and requires the viewer to have it installed. For anything public, use Set EmbedFont path/to/font.woff2 — it embeds the glyphs so rendering is identical everywhere.

Can I get a GIF or MP4?

Not today — DVD outputs SVG. If you need a raster video for social media or slides, use VHS.

Does dvd rec work on Windows?

Recording uses a PTY via node-pty. On Windows it falls back to COMSPEC (cmd.exe). SVG rendering itself needs no PTY and works everywhere Node 18+ runs.

What exactly are the dependencies?

Rendering needs no external binaries — no ffmpeg, no headless browser, no video encoder. That is the claim.

The npm package does have normal Node dependencies: the dvdrw rendering library, shellfie, yargs, and node-pty (a native module used only by dvd rec).


Related

  • dvd — the rendering library behind this CLI
  • shellfie — static terminal screenshots as SVG
  • shellfie-cli — the screenshot CLI
  • chartscii — ASCII charts, great input for DVD
  • VHS — GIF/MP4 terminal recordings

Contributing

Issues and PRs welcome.

git clone https://github.com/tool3/dvd-cli
cd dvd-cli
npm install
npm run build
npm test

MIT © tool3

About

🎬 create beautiful animated SVGs from terminal output

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages