| name | demodsl-generate |
| description | Generate and run DemoDSL demo configurations. Use when: creating a YAML demo config, building a product demo scenario, generating browser automation with narration and effects, running demodsl, scaffolding a new demo file. |
| argument-hint | Describe the demo you want (URL, actions, effects, narration style) |
DemoDSL Config Generator & Runner
Generate valid DemoDSL YAML configurations from a natural language description, then validate and execute them.
When to Use
- User wants to create a product demo video from a URL
- User asks to generate a YAML/JSON config for demodsl
- User wants to add scenarios, effects, narration, or avatars to a demo
- User asks to run or execute a demo configuration
- User wants to create a mobile app demo (Android/iOS)
Procedure
Step 1 — Understand the request
Extract from the user's description:
- Target URL(s) to demo
- Actions to perform (navigate, click, type, scroll, screenshot)
- Effects desired (spotlight, confetti, glow, camera movements, etc.)
- Narration style and content
- Features to enable (avatar, subtitles, popup cards, cursor, glow select)
- Output preferences (format, resolution, filename)
- Languages — does the user want multi-language audio/subtitles? If yes, capture the source language + target languages (see "Multi-language tracks" below)
- Platform: browser demo or mobile app demo (Android/iOS)
- Framework (for mobile): Expo, React Native, Flutter, Capacitor, Cordova, native Android/iOS, MAUI, NativeScript
If the user doesn't specify details, use sensible defaults:
- Browser:
webkit, Viewport: 1280x720
- Voice:
gtts engine (free, no API key)
- Pipeline:
generate_narration → edit_video
- Mobile: detect framework from project files (see mobile-frameworks.md)
Step 2 — Generate the YAML config
Build the config following these rules:
Required sections
metadata:
title: "..."
version: "2.0.0"
scenarios:
- name: "..."
url: "..."
steps: [...]
pipeline:
- generate_narration: {}
- edit_video: {}
Structure rules
- Every step needs an
action field: navigate, click, type, scroll, wait_for, or screenshot
- Browser scenarios only: The first step of a scenario should be
action: "navigate" with the scenario URL. navigate is not valid in mobile scenarios — mobile apps launch automatically via bundle_id/app_package.
click and type steps need a locator with type (css/id/xpath/text) and value
scroll steps need direction (up/down/left/right) and pixels
narration is a free-text string on each step — it becomes TTS audio
wait on a step sets a pause in seconds after execution
effects is a list on each step — each has a type and optional params
pre_steps is an optional list on a scenario — steps executed before recording starts (useful for page loading, waiting for assets, login flows, etc.). If a pre-step fails, a debug screenshot is automatically saved.
char_rate on a type step enables organic (character-by-character) typing at N chars/second
zoom_input on a type step zooms the viewport into the target input during typing (true for defaults, or {scale: 1.5, padding: 50} for custom)
natural: true on a scenario enables all natural-feel defaults (smooth scroll, hover delay, typing variance, Bézier cursor, timing jitter). Override individual values with a NaturalConfig object.
- Per-step overrides:
hover_delay (pause before click), smooth_scroll (CSS smooth scrolling), typing_variance (random speed variation when char_rate is set)
Mobile scenario rules
When the user wants a mobile app demo, use mobile: instead of url: on the scenario:
- Detect the user's app framework from project files (see mobile-frameworks.md for detection markers and per-framework config)
- Set
mobile.platform to android or ios
- Read the app's package/bundle ID from the project config files (e.g.,
app.json for Expo, build.gradle for Android, etc.)
- Use
accessibility_id as the default locator type — it's cross-platform and works with all frameworks. However, on iOS/Expo, accessibility_id may not work if the app wasn't built with explicit accessibilityIdentifier props. When locators fail, fall back to: accessibility_id → ios_predicate → ios_class_chain → coordinates (x/y or start_x/start_y).
- Mobile-only actions:
tap, swipe, pinch, long_press, back, home, notification, app_switch, rotate_device, shake
tap accepts either a locator or coordinates (x/y or start_x/start_y). Use coordinates when locators don't work:
- action: "tap"
x: 197
y: 310
narration: "Tap the button"
- action: "tap"
start_x: 197
start_y: 310
swipe requires start_x, start_y, end_x, end_y (and optionally duration_ms)
pinch requires pinch_scale (>1.0 = zoom in, <1.0 = zoom out) and a locator or coordinates
rotate_device requires orientation: "portrait" or "landscape"
- Browser-only features (cursor, glow_select, popup_card, DOM effects) do NOT apply to mobile scenarios
screenshot, scroll, type, wait_for work identically in both browser and mobile contexts
- For Expo Go apps, add
pre_steps to navigate from Expo Go's project list to the actual app
- Do NOT use
navigate in mobile scenarios — the app launches automatically. Using navigate will cause a validation error.
- Always set
automation_name explicitly: "XCUITest" for iOS, "UiAutomator2" for Android
scenarios:
- name: "App walkthrough"
mobile:
platform: "android"
device_name: "emulator-5554"
app_package: "com.example.myapp"
app_activity: "com.example.myapp.MainActivity"
viewport:
width: 1080
height: 2400
steps:
- action: "wait_for"
locator: { type: "accessibility_id", value: "home-screen" }
timeout: 10
narration: "The app launches and shows the home screen."
- action: "tap"
locator: { type: "accessibility_id", value: "get-started" }
narration: "Tap the get started button."
- action: "tap"
x: 197
y: 310
narration: "Tap the element at specific coordinates."
- action: "swipe"
start_x: 800
start_y: 1200
end_x: 200
end_y: 1200
narration: "Swipe to see the next page."
Pre-steps (warmup without recording)
Use pre_steps to run actions before recording begins. This is useful for initial page loads, waiting for heavy assets, or any setup that should not appear in the final video:
scenarios:
- name: "My demo"
url: "https://example.com"
pre_steps:
- action: "navigate"
url: "https://example.com"
- action: "wait_for"
locator: { type: "css", value: "#app-loaded" }
timeout: 10
wait: 2
steps:
- action: "click"
locator: { type: "css", value: "#start-btn" }
narration: "Click here to begin"
⚠️ Mobile : navigate is a browser-only action and is rejected in mobile scenarios (at parse time). Mobile apps launch automatically via bundle_id/app_package. Use tap, wait_for, or swipe in mobile pre_steps.
Pipeline ordering
The pipeline stages must be ordered logically:
restore_audio — denoise/normalize/EQ/compression/de-essing/reverb/silence-removal (optional)
restore_video — stabilize/sharpen (optional)
color_correction — brightness/contrast/saturation/gamma/white balance (optional)
apply_effects — post-processing effects (optional)
generate_narration — TTS audio generation (if narration used)
render_device_mockup — 3D device frame (optional)
composite_avatar — avatar overlay (if avatar enabled)
edit_video — intro/outro/transitions/watermark composition
burn_subtitles — subtitle burning (if subtitles enabled)
mix_audio — voice + background music (if background music used)
speed — global speed adjustment (optional)
fit_duration — adjust speed to match a target duration (optional)
frame_rate — frame rate conversion (optional)
pip — picture-in-picture overlay (optional)
chapters — chapter markers (optional)
thumbnail — thumbnail extraction (optional)
optimize — format/codec/quality conversion (optional)
Voice engines
gtts — Free, no API key, use for testing/dev
elevenlabs — High quality, needs ELEVENLABS_API_KEY
openai — Good quality, needs OPENAI_API_KEY
gradium — Low-latency multilingual (en/fr/de/es/pt), needs GRADIUM_API_KEY
espeak — Offline, robotic voice, no API key
- Others:
google, azure, aws_polly, cosyvoice, coqui, piper, local_openai, custom
Default to gtts unless the user requests a specific engine.
Step 3 — Write the file
Save the generated YAML to the workspace. Default filename: demo.yaml in the project root, or use the name the user provides.
Step 4 — Validate
Run validation:
cd /Users/famat/PycharmProjects/SIDE/demodsl && python -m demodsl validate <config_file>
If validation fails, fix the YAML and retry.
Step 5 — Execute
Run the demo:
cd /Users/famat/PycharmProjects/SIDE/demodsl && python -m demodsl run <config_file> --output-dir output/
Add --skip-voice if the user wants a quick test without TTS.
Add --dry-run if the user just wants to verify the config.
Add --verbose for debug logging.
Add --force (alias for --no-run-cache) to re-record everything from scratch.
Add --turbo for fast preview generation (minimal waits, skips avatars, 3D rendering, subtitles, post-effects, speed re-encode).
Mobile diagnostic commands
Before running a mobile demo, verify the Appium connection:
python -m demodsl test-connection <config_file>
This starts a session, takes a screenshot, shows the screen resolution, and disconnects.
To dump the accessibility tree of the current screen (useful for finding locators):
python -m demodsl inspect <config_file>
python -m demodsl inspect <config_file> --raw
Feature Configuration Quick Reference
Avatar (animated narrator in corner)
avatar:
enabled: true
provider: "animated"
position: "bottom-right"
size: 100
style: "bounce"
shape: "circle"
Add composite_avatar: {} to the pipeline BEFORE edit_video.
Subtitles
subtitle:
enabled: true
style: "classic"
position: "bottom"
Add burn_subtitles: {} to the pipeline AFTER edit_video.
Popup cards
popup_card:
enabled: true
theme: "glass"
animation: "slide"
Then on steps, add a card field with title, body, items, icon.
Cursor customization
cursor:
visible: true
style: "dot"
color: "#ef4444"
click_effect: "ripple"
Glow select (element hover glow)
glow_select:
enabled: true
colors: ["#a855f7", "#6366f1", "#ec4899"]
Background music + audio
audio:
background_music:
file: "audio/music.mp3"
volume: 0.3
ducking_mode: "moderate"
voice_processing:
enhance_clarity: true
enhance_warmth: false
de_ess: true
noise_reduction: true
noise_reduction_strength: "moderate"
remove_silence: true
min_silence_duration: 0.5
effects:
eq_preset: "podcast"
compression:
preset: "voice"
reverb_preset: "small_room"
Add restore_audio: {} (before narration) and mix_audio: {} to the pipeline.
Color correction
video:
color_correction:
brightness: 0.1
contrast: 0.2
saturation: 1.2
gamma: 1.0
white_balance: "daylight"
temperature: 5600
Add color_correction: {} to the pipeline.
Speed control
Per-step speed: add speed: 0.5 (slow-mo) or speed: 2.0 (fast) on any step.
Speed ramp: add speed_ramp: {start_speed: 1.0, end_speed: 0.5, ease: "ease-in-out"}.
Freeze frame: add freeze_duration: 3.0 on a step.
Audio offset (J/L cuts): add audio_offset: -0.5 (J-cut) or audio_offset: 0.5 (L-cut).
Global speed: add speed: {speed: 1.5} to the pipeline.
Fit duration (make it fit)
Adjust the video speed to exactly match a target duration:
pipeline:
- fit_duration:
target_duration: 60
strategy: "any"
min_speed: 0.25
max_speed: 4.0
strategy: "any" — speed up or slow down as needed (default)
strategy: "speed_up" — only make the video shorter; skip if already shorter than target
strategy: "slow_down" — only make the video longer; skip if already longer than target
min_speed / max_speed — clamp the computed speed factor to keep the result watchable
Place fit_duration in the pipeline after edit_video and speed but before optimize.
Organic typing (human-like character-by-character input)
- action: "type"
locator: { type: "css", value: "#search" }
value: "what is DemoDSL?"
char_rate: 8
zoom_input: true
narration: "Let me type a search query"
Custom zoom settings:
zoom_input:
scale: 2.0
padding: 80
char_rate alone gives organic typing without zoom.
zoom_input alone gives zoom with instant fill.
- Both combined give the full cinematic typing experience.
Natural-feel mode (human-like demo behavior)
scenarios:
- name: "natural demo"
url: "https://example.com"
natural: true
steps:
- action: "click"
locator: { type: "css", value: "#btn" }
- action: "scroll"
direction: "down"
pixels: 400
- action: "type"
locator: { type: "css", value: "#search" }
value: "hello"
char_rate: 8
Custom tuning:
natural:
hover_delay: 0.3
smooth_scroll: true
jitter: 0.15
typing_variance: 0.4
bezier_cursor: true
Per-step overrides (hover_delay, smooth_scroll, typing_variance) take precedence over scenario-level natural defaults.
Picture-in-Picture
video:
pip:
source: "webcam_recording.mp4"
position: "bottom-right"
size: 0.25
shape: "rounded"
opacity: 0.95
Add pip: {} to the pipeline.
Social export profiles
output:
social:
- platform: "youtube"
- platform: "instagram_reels"
crop_mode: "center"
- platform: "tiktok"
- platform: "twitter"
- platform: "linkedin"
thumbnails:
- auto: true
format: "png"
- timestamp: 5.0
format: "jpeg"
Add thumbnail: {} and chapters: {} to the pipeline if needed.
Turbo mode (fast preview)
Use --turbo on the CLI to generate a quick preview. The config itself doesn't change — turbo is a runtime flag:
demodsl run demo.yaml --turbo
What turbo skips:
- Avatar generation & compositing
- 3D device rendering (Blender)
- Post-processing effects (freeze frames, speed ramps)
- Global speed re-encode
- Subtitle burning
- Watermark overlay
What turbo keeps:
- Browser recording (all steps execute normally)
- Narration generation (TTS)
- Browser effects (spotlight, highlight, glow, etc.)
- Basic video editing (intro/outro/transitions)
Wait behavior: All time.sleep() pauses (step waits, hover delays, scroll sync) are clamped to 50ms, making the recording dramatically faster.
Workflow: Use --turbo to iterate on step flow & narration quickly, then remove it for the final high-quality render.
Multi-language tracks (multilang) — v2.7+
Generate a single MP4 with multiple audio + subtitle tracks (one per language) — or write sidecar files. The scenario is recorded once; narration is synthesised in each requested language.
Top-level languages: block
languages:
default: "fr"
targets: ["en", "de", "es"]
embed: true
burn_default: false
audio_only: false
subtitle_only: false
voices:
en: { engine: "gtts", voice_id: "en" }
de: { engine: "elevenlabs", voice_id: "german_voice_id" }
Per-step translations (narrations:)
Each step gains an optional narrations mapping. Keys are BCP-47 codes; values are translated text. Missing translations fall back to the base narration: field automatically.
steps:
- narration: "Bienvenue sur notre site."
narrations:
en: "Welcome to our website."
de: "Willkommen auf unserer Website."
es: "Bienvenido a nuestro sitio."
action: "scroll"
direction: "down"
pixels: 400
Embedded vs sidecar output
embed: true (default) — the final MP4 contains:
- One AAC audio track per language (with
language= metadata, default-disposition on the source language)
- One
mov_text subtitle track per language
- VLC, QuickTime, YouTube, Vimeo all expose them as selectable tracks
embed: false — produces a regular single-track MP4 plus sidecar files in the output dir:
narration_en.mp3, narration_de.mp3, ...
subtitles_en.ass, subtitles_de.ass, ...
- Useful when uploading to platforms requiring external caption files
Important behaviors
- Subtitle burn-in is auto-skipped when
languages is active (so the picture stays clean for embedded subs). Re-enable with languages.burn_default: true.
- TTS cache is language-tagged — re-runs are fast and per-language.
- Per-language voices inherit from top-level
voice: for any field omitted in the override.
- ffmpeg is required for muxing. On failure, the engine gracefully falls back to a single-track export.
- Skipped under
--turbo, --dry-run, and --separate-audio.
Rules of thumb
- Always set
languages.default explicitly — don't rely on the implicit "en".
- Use BCP-47 codes (
en, fr, en-US, pt-BR) — max 8 chars.
- For each step, write the source-language text in
narration: and translations in narrations:.
- If voices differ per language, set them in
languages.voices (don't try to switch the top-level voice:).
- For social platforms that don't read embedded sub tracks (Instagram, TikTok), use
embed: false and upload the sidecar .ass separately, OR set burn_default: true and re-render per-language clips.
Complete example
metadata:
title: "Multilang demo"
version: "2.0.0"
voice:
engine: "gtts"
voice_id: "fr"
languages:
default: "fr"
targets: ["en", "de"]
embed: true
voices:
en: { engine: "gtts", voice_id: "en" }
de: { engine: "gtts", voice_id: "de" }
scenarios:
- name: "tour"
url: "https://example.com"
steps:
- action: "navigate"
url: "https://example.com"
- narration: "Bienvenue sur notre site."
narrations:
en: "Welcome to our website."
de: "Willkommen auf unserer Website."
action: "scroll"
direction: "down"
pixels: 400
pipeline:
- generate_narration: {}
- edit_video: {}
Run as usual:
demodsl run demo_multilang.yaml
demodsl run demo_multilang.yaml --dry-run
See examples/demo_multilang.yaml for the canonical reference file.
References