Skip to main content

teasr

Active

Capture showcase screenshots and GIFs from web apps, desktop, and terminal. Single Rust binary, no runtime deps.

Rust 2Updated Apr 20, 2026
automationcapturechrome-devtools-protocolclideveloper-toolsffmpeggifmarkdownrustscreenshotshowcaseterminalvideo

teasr

Automated project showcase capture — screenshots and GIFs from web apps, desktop, and terminal. Single binary, no runtime dependencies.

Download · Report Bug · CI Integration

CI crates.io   License

Showcase

Web Capture Terminal Capture
Web capture of this project's GitHub page Terminal capture of CLI help
Local HTML Markdown
Local HTML file rendered via headless Chrome Markdown rendered as styled HTML via headless Chrome

Contents

Why teasr

teasrNode/Playwright approach
RuntimeSingle binaryNode.js + npm install
Terminal renderBuilt-in (ANSI → SVG → PNG)External tools
GIF encodinggifski (pure Rust)FFmpeg or ImageMagick
Configteasr.tomlJS/TS config file
Server cleanupProcess group killManual or best-effort

Installation

Shell installer (recommended):

curl -fsSL https://raw.githubusercontent.com/urmzd/teasr/main/install.sh | sh

Cargo:

cargo install teasr-cli

GitHub Action: see CI Integration below.

Quick Start

Create teasr.toml in your project root:

[server]
command = "npm run dev"
url = "http://localhost:3000"
timeout = 10000

[output]
dir = "./showcase"
formats = [{ output_type = "png" }]

[[scenes]]
type = "web"
uri = "/"
name = "homepage"
formats = [{ output_type = "gif" }, { output_type = "png" }]

[[scenes.interactions]]
type = "snapshot"

[[scenes.interactions]]
type = "click"
selector = "#get-started"

[[scenes.interactions]]
type = "snapshot"

[[scenes]]
type = "terminal"
name = "cli-help"
theme = "dracula"
cols = 90
rows = 24
formats = [{ output_type = "gif" }, { output_type = "png" }]

[[scenes.interactions]]
type = "type"
text = "teasr --help"
speed = 50

[[scenes.interactions]]
type = "key"
key = "enter"

[[scenes.interactions]]
type = "wait"
duration = 2000

Then run:

teasr run

Output files are written to ./showcase/.

Capture Modes

All three capture modes — terminal, web, screen — use a unified [[scenes.interactions]] syntax. Every interaction type is accepted by every mode; unsupported interactions are silently skipped (visible with --verbose). The web scene loads remote URLs, local files (HTML/SVG/PDF), or Markdown files through the same headless-Chrome renderer.

Interaction Types

TypeFieldsDescription
typetext, speed (ms per char, optional)Type text (terminal: PTY input, web: keyboard events)
keykey (e.g. "enter")Press a key
clickselector (CSS selector, optional)Click an element
hoverselector (CSS selector, optional)Hover over an element
scroll-toselector (CSS selector, optional)Scroll an element into view
waitduration (ms, default 1000)Pause. Terminal/screen emit one frame at the end of the pause (duration_ms = duration); web emits nothing — follow with snapshot to capture post-pause state.
snapshotname (optional)Capture the current state as a frame

Every interaction also accepts a hidden flag (default false). Hidden interactions execute normally but their frames are excluded from output — useful for setup steps (e.g. typing a command) that should not appear in the final GIF or screenshot.

[[scenes.interactions]]
type = "type"
text = "cd my-project"
hidden = true

[[scenes.interactions]]
type = "key"
key = "enter"
hidden = true

[[scenes.interactions]]
type = "wait"
duration = 500
hidden = true

Web

Loads a URI in headless Chrome (via chromiumoxide). The uri field picks what to load:

  • http:// / https:// — remote URL (a leading / joins against [server].url if configured)
  • *.md / *.markdown — Markdown file rendered to styled HTML
  • anything else — local file (HTML, SVG, PDF) loaded via file://

Requires Chrome or Chromium to be installed.

# Remote URL (or server-relative path with [server])
[[scenes]]
type = "web"
uri = "/dashboard"
name = "dashboard"
viewport = { width = 1440, height = 900 }
formats = [{ output_type = "png" }, { output_type = "gif" }]

[[scenes.interactions]]
type = "click"
selector = "#open-modal"

[[scenes.interactions]]
type = "snapshot"
name = "modal-open"

# Local HTML / SVG
[[scenes]]
type = "web"
uri = "./docs/preview.html"
name = "docs-preview"

[[scenes.interactions]]
type = "snapshot"

# PDF (page selection via the `page` field)
[[scenes]]
type = "web"
uri = "./spec.pdf"
page = 2

[[scenes.interactions]]
type = "snapshot"

# Markdown (rendered with the bundled GitHub-style template)
[[scenes]]
type = "web"
uri = "./README.md"
theme = "dark"           # "light" (default) or "dark"
flavor = "github"        # "github" (default), "commonmark", or "custom"
full_page = true

[[scenes.interactions]]
type = "snapshot"

Web scene fields:

FieldTypeDefaultDescription
uristringrequiredRemote URL, server-relative path, local file, or Markdown file
namestringuri valueOutput filename base
viewportobject1280x720{ width, height }
formatsarrayoutput.formatsPer-scene format override
interactionsarray[]Sequence of interactions
full_pagebooleanfalseCapture full page height instead of just the viewport
frame_durationinteger100Milliseconds per frame in GIF output
pageinteger1PDF page to capture (only applies when uri is a PDF)
themestring"light"Markdown theme: "light" or "dark" (Markdown only)
flavorstring"github"Markdown flavor: "github", "commonmark", "custom" (Markdown only)
stylesheetstringPath to a custom CSS file appended after the default styles (Markdown only)
templatestringPath to a full HTML template with {{content}} placeholder; overrides the default template (Markdown only)

Markdown flavor details:

FlavorBehavior
githubGitHub Flavored Markdown — tables, task lists, autolinks, strikethrough, footnotes
commonmarkStrict CommonMark — no extensions
customGFM extensions enabled; pair with stylesheet to apply your own visual style

Supported interactions: click, hover, scroll-to, wait, snapshot, type, key

Terminal

Scripts an interactive PTY session, captures frames at each interaction, and renders them as animated GIFs or PNGs with terminal chrome (title bar, traffic light buttons).

[[scenes]]
type = "terminal"
name = "test-output"
theme = "dracula"
cols = 100
rows = 24
formats = [{ output_type = "gif" }, { output_type = "png" }]
frame_duration = 80

[[scenes.interactions]]
type = "type"
text = "cargo test 2>&1"
speed = 50

[[scenes.interactions]]
type = "key"
key = "enter"

[[scenes.interactions]]
type = "wait"
duration = 2000

Terminal scene fields:

FieldTypeDefaultDescription
namestring"recording"Output filename base
themestring"dracula""dracula" or "monokai"
colsinteger80Terminal width in columns
rowsinteger24Terminal height in rows
interactionsarray[]Sequence of interactions
frame_durationinteger100Milliseconds per frame in GIF output
formatsarrayoutput.formatsPer-scene format override

Supported interactions: type, key, wait, snapshot

Screen

Captures a display, window, or region using native screen capture (xcap). Screenshots are automatically wrapped in macOS-style window chrome (matching terminal output). Supports multi-frame GIF output when multiple snapshot + wait interactions are configured.

[[scenes]]
type = "screen"
name = "native-app"
setup = "open MyApp.app"
delay = 2000
theme = "dracula"
title = "My App"
formats = [{ output_type = "gif" }, { output_type = "png" }]

[[scenes.interactions]]
type = "snapshot"

[[scenes.interactions]]
type = "wait"
duration = 1000

[[scenes.interactions]]
type = "snapshot"

Screen scene fields:

FieldTypeDefaultDescription
namestring"screen"Output filename base
displayintegerprimaryDisplay index (ignored if window is set)
windowstringWindow title or app name substring (case-insensitive)
regionobjectfull display{ x, y, width, height }
setupstringShell command run before capture
delayintegerMilliseconds to wait after setup
interactionsarray[]Sequence of interactions
frame_durationinteger100Milliseconds per frame in GIF output
titlestring"Screen Capture"Title shown in chrome frame title bar
themestring"dracula"Chrome frame theme: "dracula" or "monokai"
formatsarrayoutput.formatsPer-scene format override

Supported interactions: snapshot, wait

Configuration Reference

[server]

Optional. Starts a process before capture and health-polls it until ready. The process group is killed on exit — no orphaned processes.

[server]
command = "npm run dev"
url = "http://localhost:3000"
timeout = 10000          # ms to wait for server to be ready (default: 10000)

[output]

[output]
dir = "./showcase"       # default: "./teasr-output"
formats = [{ output_type = "png" }]  # default: [{ output_type = "png" }]. Options: "png", "gif", "mp4"

[[scenes]]

Each [[scenes]] entry is one of the three types described above. The type field is required and must be "web", "terminal", or "screen".

Config file discovery walks up from the current directory to the filesystem root, so running teasr from any subdirectory of your project will find teasr.toml at the root.

CLI Reference

teasr [COMMAND]

Commands:
  run     Run capture scenes from teasr.toml (alias: `showme`)
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

teasr run

teasr run [OPTIONS]

Options:
  -c, --config <PATH>      Path to teasr.toml (default: auto-discover)
  -o, --output <DIR>       Output directory (overrides config)
      --formats <FMT,...>  Output formats: png, gif, mp4 (overrides config)
      --verbose            Enable debug logging
      --timeout <MS>       Global timeout in ms [default: 60000]
      --fps <N>            Frames per second (overrides config)
      --seconds <N>        Target output duration in seconds (overrides config)
      --scene-timeout <N>  Per-scene wall-clock timeout in seconds (overrides config)
  -h, --help               Print help

--formats accepts comma-separated values: --formats png,gif,mp4

Output Formats

FormatNotes
pngLossless screenshot. Native, no external tools required.
gifAnimated GIF from multi-frame session recording, encoded with gifski (pure Rust).
mp4Video output from multi-frame session recording.

CI Integration

The GitHub Action downloads the appropriate pre-built binary from releases, installs Chrome, and runs teasr run. All configuration comes from teasr.toml.

- uses: urmzd/teasr@v1
  with:
    version: "latest"        # optional, pin to e.g. "0.11.0"
    scenes: "web,terminal"   # optional, default: all
    install-chrome: "true"   # optional, set "false" if Chrome is already available
    install-fonts: ""        # optional, space-separated font families
    config: ""               # optional, path to teasr.toml
    args: ""                 # optional, extra flags for `teasr run`

Supported runners: ubuntu-*, macos-*, windows-* on x64 and ARM64.

Workspace

teasr is a Cargo workspace with two crates:

CrateDescription
teasr-cliCLI entry point (teasr binary)
teasr-coreCapture, config, orchestration, and ANSI → SVG → PNG terminal rendering

Agent Skill

This repo’s conventions are available as portable agent skills in skills/.

License

Apache-2.0