A framework for creating scripted, shareable product demonstrations. Write YAML files to define demo steps, and DemoScript provides a polished web UI for presenting and sharing them.
- YAML-based demos - Define demos with a simple, readable schema
- Multiple step types - Slides, REST, Shell, Browser, Code, Wait, Assert, GraphQL, Database
- Variable chaining - Save values from responses and use them in subsequent steps
- Branching flows - Create choose-your-own-adventure demos with choices and navigation
- Step groups - Organize steps into collapsible sections
- Live & recorded modes - Execute live or playback pre-recorded responses
- Try It mode - Modify inputs and re-execute steps in recorded playback
- Static export - Build standalone HTML files deployable anywhere
- Gallery support - Build multiple demos with an index page and metadata badges
- Authentication - Password-protect demos with SHA-256 hashing
- Dashboard - Overview screen with health checks and demo stats
- Sidebar navigation - Collapsible step list with completion status
- Visual effects - Confetti, sounds, neon glow, animated backgrounds
- Tunnel support - Share demos publicly via ngrok or Cloudflare tunnels
- Schema validation - JSON Schema validates YAML with helpful error messages
- Live reload - Watch mode auto-refreshes on file changes
- Keyboard navigation - Arrow keys, space, and shortcuts (press
?for help) - Dark/Light mode - Toggle theme support
- Mobile responsive - Works on tablets and phones
See DemoScript in action without installing:
- Hello World - Minimal starter demo
- JSONPlaceholder - REST API tutorial
- GitHub API - Custom link handlers
- Feature Showcase - All features demo
Browse all examples at demoscript.app/gallery
npm install -g demoscript-cliThen run with:
demoscript serve ./my-demoOr run directly with npx (no install required):
npx demoscript-cli serve ./my-demoRPM and DEB packages are available in Releases.
# RHEL/Alma/Fedora
sudo rpm -ivh demoscript-1.0.0.noarch.rpm
# Ubuntu/Debian
sudo dpkg -i demoscript_1.0.0_all.debRequires: Node.js >= 18
- Create a demo file
demo.yaml:
title: "My API Demo"
description: "Demonstrate our API capabilities"
settings:
base_url: "https://api.example.com"
steps:
- slide: |
# Welcome
This demo shows our API in action.
- rest: GET /users/1
title: "Fetch User"
save:
userId: id
userName: name
- slide: |
# Done!
Fetched user: $userName (ID: $userId)- Run the demo:
demoscript serve ./demo.yaml- Or build a static site:
demoscript build ./demo.yaml -o distStart a development server with live execution.
demoscript serve examples/hello-world --port 3000 --hostOptions:
-p, --port <port>- Port number (default: 3000)-H, --host [host]- Bind to 0.0.0.0 for LAN access-w, --watch- Watch demo files and auto-reload on changes--no-open- Don't auto-open browser--tunnel [provider]- Create public tunnel (ngrok or cloudflare)--tunnel-auth <user:pass>- HTTP basic auth for tunnel (ngrok only)--tunnel-name <name>- Named Cloudflare tunnel--tunnel-hostname <host>- Custom hostname for named tunnel
Tunnel examples:
# Quick public URL with ngrok (default)
demoscript serve ./my-demo --tunnel
# Public URL with Cloudflare (no account required)
demoscript serve ./my-demo --tunnel cloudflare
# Password-protected tunnel
demoscript serve ./my-demo --tunnel --tunnel-auth "user:secret"
# Named Cloudflare tunnel with custom domain
demoscript serve ./my-demo --tunnel-name mytunnel --tunnel-hostname demo.example.comExecute all steps and save responses to recordings.json.
demoscript record examples/hello-world -o recordings.jsonOptions:
-o, --output <file>- Output filename (default: recordings.json)
Export demo as a static site.
# Single demo
demoscript build examples/hello-world -o dist
# All demos with gallery index
demoscript build examples --all -o distOptions:
-o, --output <dir>- Output directory (default: dist)--all- Build all demos in directory with gallery index
Deploy demo to Netlify with one command.
# Draft deploy (preview URL)
demoscript deploy examples/hello-world
# Production deploy
demoscript deploy examples/hello-world --prod
# Deploy to specific site
demoscript deploy examples/hello-world --prod --site my-demo-siteOptions:
-o, --output <dir>- Build output directory (default: dist)--prod- Deploy to production (default is draft preview)-s, --site <name>- Netlify site name or ID
The deploy command will:
- Check for Netlify CLI (installs if missing)
- Authenticate with Netlify (if not logged in)
- Record the demo (if no recordings.json exists)
- Build the static site
- Deploy to Netlify
Export demo as an MP4 video.
demoscript export-video examples/browser-demo -o demo.mp4Options:
-o, --output <file>- Output video file (default: demo.mp4)-w, --width <pixels>- Video width (default: 1280)-h, --height <pixels>- Video height (default: 720)-f, --fps <number>- Frames per second (default: 30)--delay <ms>- Delay between steps in milliseconds (default: 2000)
Note: Requires a static build first (demoscript build).
Export demo as an animated GIF.
demoscript export-gif examples/browser-demo -o demo.gif --optimizeOptions:
-o, --output <file>- Output GIF file (default: demo.gif)-w, --width <pixels>- GIF width (default: 800)-f, --fps <number>- Frames per second (default: 10)--delay <ms>- Delay between steps in milliseconds (default: 2000)--optimize- Use two-pass encoding with palette generation (slower but better quality)
Note: Requires a static build first (demoscript build).
title: "Demo Title" # Required
description: "Description" # Optional
version: "1.0" # Optional
author: "Author Name" # Optional
tags: [tag1, tag2] # Optional
# Gallery metadata (shown in gallery index)
metadata:
duration: "5 minutes" # Estimated demo duration
difficulty: intermediate # beginner | intermediate | advanced
category: "Tutorial" # Category for organization
settings:
base_url: "https://api.example.com"
openapi: "https://api.example.com/docs/json" # OpenAPI spec URL
polling:
interval: 2000 # Default poll interval (ms)
max_attempts: 30 # Default max poll attempts
steps:
# Step definitions...Gallery metadata is displayed as badges when building with --all. Difficulty levels show colored badges (green/yellow/red).
Display markdown content:
- slide: |
# Heading
Markdown content with **formatting** and $variables
title: "Optional Title"Make HTTP API calls:
- rest: POST /api/endpoint
title: "Create Resource"
description: "Optional description"
show_curl: true # Display curl command for this request
headers:
Authorization: "Bearer $token"
defaults: # Set defaults for OpenAPI-generated fields
fieldName: "default value"
form: # Manual form fields (override OpenAPI)
- name: fieldName
label: "Display Label"
default: "default value"
type: text # text, number, select, textarea
required: true
readonly: false
body: # Alternative to form
key: "value"
save:
varName: response.path.to.value
httpStatus: _status # Special keyword: HTTP status code (200, 404, etc.)
results:
- key: id
label: "ID"
- key: txHash
type: tx # Special type with explorer link
link: polygonscan # Links to configured explorer
poll:
endpoint: "/api/status/$jobId"
success_when: "response.status == 'complete'"
failure_when: "response.status == 'error'"
interval: 2000
max_attempts: 30REST save keywords:
| Keyword | Description |
|---|---|
_status |
HTTP status code (200, 404, 500, etc.) |
path.to.value |
JSON path extraction from response body |
Result types:
| Type | Description |
|---|---|
text |
Plain text (default) |
address |
Identifier with copy button and optional link |
tx |
Transaction/reference ID with optional link |
token |
Token/resource identifier with optional link |
currency |
Formatted currency value |
code |
Syntax-highlighted code block |
json |
Formatted JSON with syntax highlighting |
table |
Render array data as a table |
link |
Clickable URL |
Custom link handlers:
Configure link handlers for any external service (GitHub, Jira, blockchain explorers, etc.):
settings:
links:
# GitHub links
github:
user: "https://github.com/{value}"
issue: "https://github.com/org/repo/issues/{value}"
pr: "https://github.com/org/repo/pull/{value}"
# Jira links
jira:
issue: "https://mycompany.atlassian.net/browse/{value}"
# Blockchain explorer
polygonscan:
address: "https://amoy.polygonscan.com/address/{value}"
tx: "https://amoy.polygonscan.com/tx/{value}"Then reference in results:
results:
- key: username
type: address
link: github # Opens https://github.com/{username}
- key: issueNumber
type: tx
link: jira # Opens Jira issue
- key: contractAddress
type: address
link: polygonscan # Opens blockchain explorerExecute shell commands:
- shell: echo "Hello World"
title: "Run Command"
shell_type: bash # bash, powershell, cmd
workdir: ./scripts
env:
MY_VAR: "value"
confirm: true # Require user confirmation
save:
result: stdout # Save standard output to $result
errors: stderr # Save standard error to $errors
exitCode: status # Save exit code to $exitCode (0 = success)Shell save keywords:
| Keyword | Description |
|---|---|
stdout |
Standard output (recommended) |
stderr |
Standard error output |
status |
Exit code (0 = success, non-zero = error) |
output |
Legacy alias for stdout |
Example capturing multiple outputs:
- shell: ls -la /tmp 2>&1
title: "List Directory"
save:
listing: stdout # The directory listing
exitCode: status # 0 if successfulOpen URLs in the browser:
- browser: https://example.com/dashboard/$userId
title: "View Dashboard"
description: "Open the user dashboard in browser"In live mode, clicking "Open in Browser" launches the URL in the system default browser. Variables are substituted in the URL.
During recording (demoscript record), browser steps automatically capture a screenshot of the page using Puppeteer. Screenshots are saved to assets/screenshots/ and displayed in playback mode.
Display syntax-highlighted code snippets:
- code: |
const user = await api.getUser(id);
console.log(user.name);
language: javascript
filename: example.js
title: "Code Example"
highlight: [1, 2] # Optional: highlight specific linesSupported languages: javascript, typescript, python, bash, json, yaml, sql, go, rust, java, c, cpp, and more.
Add timed delays with visual countdown:
- wait: 2000 # Duration in milliseconds
message: "Processing..." # Optional message
title: "Brief Pause"Validate conditions with pass/fail display:
- assert: "$userId == 1"
title: "Verify User ID"
description: "Ensure we fetched the correct user"
message: "Custom failure message"Supported operators: ==, !=, >, >=, <, <=
Supports nested paths: $response.data.id == 123
Execute GraphQL queries:
- graphql: |
query GetUser($id: ID!) {
user(id: $id) { name, email }
}
endpoint: "https://api.example.com/graphql"
variables:
id: "$userId"
save:
userName: data.user.name
title: "Fetch User"Query MongoDB, PostgreSQL, or MySQL databases:
# MongoDB
- db: findOne
collection: users
query: { email: "$userEmail" }
save:
userName: name
title: "Find User"
# SQL
- db: query
type: postgres
query: "SELECT * FROM users WHERE id = $userId"
save:
userName: nameMongoDB operations: find, findOne, insertOne, updateOne, deleteOne
Create choose-your-own-adventure demos with step IDs, goto navigation, and user choices.
Give a step an ID to jump to it from elsewhere:
- id: summary
slide: |
# Summary
Demo complete!Automatically jump to another step after completing:
- code: |
// Quick example
title: "Code Sample"
goto: summary # Jump to summary step after thisPresent users with options that navigate to different paths:
- slide: |
# Choose Your Path
Would you like a quick overview or deep dive?
title: "Choose Path"
choices:
- label: "Quick Overview"
description: "Jump to the summary"
goto: summary
- label: "Deep Dive"
description: "See all the details"
goto: deep-diveOrganize related steps into collapsible groups:
steps:
- group: "Setup"
description: "Initial configuration steps"
collapsed: false
steps:
- slide: |
# Setup
Let's configure the environment.
- shell: echo "Setting up..."
- group: "Main Flow"
steps:
- rest: POST /api/start
title: "Start Process"Groups provide visual organization in the stepper UI and can be collapsed to hide completed sections.
Use $variableName to reference saved values:
- rest: GET /users/$userId
headers:
Authorization: Bearer $token
form:
- name: email
default: "$userEmail"Variables are extracted using save: with dot notation paths:
save:
userId: id # response.id
userName: data.user.name # response.data.user.nameDemoScript can automatically generate form fields from OpenAPI/Swagger specifications. This reduces manual configuration and ensures forms stay in sync with your API.
Setup: Add the OpenAPI spec URL to your settings:
settings:
base_url: "https://api.example.com"
openapi: "https://api.example.com/docs/json"Automatic form generation: When an OpenAPI spec is configured, REST step forms are automatically generated from the request body schema:
# Before: Manual form definition
- rest: POST /users
form:
- name: name
type: text
required: true
- name: email
type: text
required: true
- name: age
type: number
# After: Just set defaults
- rest: POST /users
defaults:
name: "John Doe"
email: "john@example.com"Priority rules (smart merge):
- OpenAPI schema - Base form fields (types, required flags, descriptions)
defaults:- Override default values onlyform:- Partial override (only specified properties are changed, rest inherited)
Fields in form: inherit from OpenAPI + defaults:, so you only need to specify what you want to change:
# OpenAPI provides: name, type, required
# defaults provides: default value
# form overrides: just the label
- rest: POST /plvs/$plvId/deposits
defaults:
amount: 1000
wallet: "0x70997970..."
form:
- name: wallet
label: "From Wallet" # Only override label, keeps default from aboveType mapping:
| OpenAPI Type | Form Field Type |
|---|---|
string |
text |
integer / number |
number |
boolean |
select (true/false) |
string + enum |
select |
array / object |
textarea (JSON) |
Per-step override: Use openapi: on individual steps to override the global spec:
- rest: POST /external-api/users
openapi: "https://external-api.com/docs/json"
defaults:
name: "Default Name"Password-protect demos for restricted access:
settings:
auth:
enabled: true
password: "demo123" # Required password
message: "Enter demo password" # Custom login promptFor static builds, passwords are hashed with SHA-256 client-side. The password is never stored in plain text in the built output.
Show an overview screen when the demo loads:
settings:
dashboard:
enabled: true
show_stats: true # Step count, estimated duration
show_health: true # Service health indicators
show_description: true # Demo description
health_checks:
- name: "API Server"
url: "http://localhost:8000/health"
- name: "Database"
url: "http://localhost:5432/health"Health checks poll endpoints and show green/red status indicators. Click "Start Demo" to begin.
Add a collapsible sidebar with step list:
settings:
sidebar:
enabled: true
collapsed: false # Start expanded
show_status: true # Show completion checkmarksThe sidebar shows all steps with their titles and completion status. Click any step to jump directly to it.
Configure visual effects for engaging demos:
settings:
effects:
confetti: true # Fire confetti on step completion
sounds: true # Play success/error sounds
transitions: true # Animate step changes
counters: true # Animate numeric values
neon_glow: true # Neon text effects (dark mode)
grid_background: true # Animated grid background
glow_orbs: true # Floating glow orbs
sound_volume: 0.3 # Sound volume (0-1, default: 0.5)All effects are enabled by default for maximum engagement. Disable specific effects for a more professional presentation:
# Silent, minimal animation
settings:
effects:
confetti: false
sounds: false
# Completely disable all effects
settings:
effects:
confetti: false
sounds: false
transitions: false
counters: false
neon_glow: false
grid_background: false
glow_orbs: falseEffect descriptions:
| Effect | Description |
|---|---|
confetti |
Celebration particles when a step completes successfully |
sounds |
Audio feedback (success chord, error tone) |
transitions |
Slide animation when navigating between steps |
counters |
Animated number counting for numeric result values |
neon_glow |
Glowing neon text effects in dark mode |
grid_background |
Animated perspective grid background |
glow_orbs |
Floating colored orbs in the background |
sound_volume |
Master volume for all sound effects (0-1) |
DemoScript supports two syntax styles for each step type:
Concise syntax (original, recommended for readability):
- rest: GET /users/1
title: "Fetch User"
save:
userId: idExplicit syntax (verbose, self-documenting):
- step: rest
method: GET
path: /users/1
title: "Fetch User"
save:
userId: idBoth syntaxes are fully supported and can be mixed within the same demo. See DESIGN.md for the complete field mapping between syntaxes.
There are several ways to share your demos with others:
Share your demo with others on the same network:
demoscript serve ./my-demo --host --port 3000This binds to 0.0.0.0 and displays both local and network URLs. Share the network URL (e.g., http://192.168.1.100:3000) with others on your LAN.
Pros: Real-time execution, interactive, immediate updates Cons: Requires server running, only accessible on same network
Deploy directly to Netlify with a single command:
# Draft deploy (get preview URL)
demoscript deploy ./my-demo
# Production deploy
demoscript deploy ./my-demo --prodThis automatically records responses (if needed), builds, and deploys. Share the Netlify URL with anyone.
Pros: One command, permanent URL, works anywhere, free hosting Cons: Requires Netlify account, pre-recorded responses only
Build a static site and host it anywhere:
# Record responses first
demoscript record ./my-demo
# Build static site
demoscript build ./my-demo -o distDeploy the dist folder to any static hosting:
- GitHub Pages
- Netlify
- Vercel
- AWS S3
- Any web server
Pros: Full control, works anywhere, permanent URL Cons: More manual steps, requires pre-recorded responses
Build multiple demos with a gallery index:
demoscript build ./examples --all -o distCreates a gallery page linking to all demos in the directory.
| Key | Action |
|---|---|
→ or Space |
Next step |
← |
Previous step |
R |
Reset demo |
? |
Show keyboard help |
Esc |
Close help dialog |
See ROADMAP.md for the full improvement guide including architecture recommendations, testing strategy, and desktop packaging plans.
- Embeddable widget - iframe embed code for docs and blogs
- Step-level reset - Re-run individual steps without full reset
- Variable editor - Modify variables during presentation
- Vercel deploy - One-command deploy to Vercel (in addition to Netlify)
- Authentication - Password-protect demos with SHA-256 hashing
- Dashboard - Overview screen with health checks and demo statistics
- Sidebar navigation - Collapsible step list with completion status
- Enhanced visual effects - Neon glow, animated grid background, floating orbs
- Tunnel support - Public URLs via ngrok or Cloudflare tunnels
- cURL display - Show curl commands for REST steps with
show_curl: true - Result types - Rich rendering for addresses, transactions, code, tables, JSON
- Custom link handlers - Configurable links for any service (GitHub, Jira, blockchain explorers)
- Gallery metadata - Duration, difficulty badges in gallery index
- Sound controls - Volume control and additional sound effects
- OpenAPI integration - Auto-generate form fields from OpenAPI/Swagger specs
- Screenshot recording - Browser steps now capture screenshots during recording
- Video export - Generate MP4 videos with
demoscript export-video - GIF export - Generate animated GIFs with
demoscript export-gif
- No integration tests for CLI commands
- Shell commands require the server to be running locally
- Video/GIF export requires static build first
# Clone repository
git clone https://github.com/your-org/demoscript
cd demoscript
# Install dependencies
npm install
# Run UI development server
npm run dev
# Build all packages
npm run build
# Run tests
cd packages/cli && npm test
cd packages/ui && npm test
# Build RPM package (requires: dnf install rpm-build)
./scripts/build-packages.sh rpm
# Build DEB package (requires: apt install dpkg-dev debhelper)
./scripts/build-packages.sh debSee CONTRIBUTING.md for detailed development guidelines.
demoscript/
├── packages/
│ ├── cli/ # CLI tool (serve, record, build, export)
│ └── ui/ # React web interface
├── examples/
│ ├── hello-world/ # Minimal starter demo
│ ├── jsonplaceholder/ # REST API tutorial
│ ├── github-api/ # Custom link handlers example
│ ├── feature-showcase/ # Comprehensive feature demo
│ └── browser-demo/ # Browser steps and export demo
├── packaging/
│ ├── rpm/ # RPM spec file
│ └── deb/ # Debian packaging files
├── scripts/
│ └── build-packages.sh # Package build script
├── demo.schema.json # JSON Schema for YAML validation
├── DESIGN.md # Architecture documentation
├── ROADMAP.md # Future plans and improvement guide
├── CONTRIBUTING.md # Development guidelines
└── CHANGELOG.md # Version history
MIT
