Modules
Automated, reproducible demo videos for Phoenix / LiveView apps.
Audio input for demos: PhxDemo.Steps.upload_audio/4,
use_microphone/3, stop_microphone/1 and use_camera(path, audio: true).
Simulated camera for demos (the machinery behind PhxDemo.Steps.use_camera/3,
use_camera_image/2 and stop_camera/1).
Builds captions (SRT / WebVTT) and chapter metadata from a demo timeline.
ExUnit case template for demos.
Visual drift checks. Warns, never fails.
Pre-flight checks so a demo fails fast with a clear message instead of recording a broken page.
Finds the files that upload/4 and drop_files/4 attach.
Waits for LiveView to settle so recordings don't capture half-updated pages and steps don't fire before the socket is connected.
MIDI background music: music: "song.mid" is rendered to WAV with
fluidsynth and the WAV then goes through the
normal music pipeline (loop/trim, fades, ducking; see PhxDemo.Render).
Generated background music: music: %{elevenlabs: "prompt"} composes a
track with the ElevenLabs Music API
and the result then goes through the normal music pipeline (loop/trim,
fades, ducking; see PhxDemo.Render).
The in-page overlay (fake cursor, click ripples, spotlight). Headless Chromium doesn't draw a cursor, so without this the video shows no pointer.
Maps a demo timeline onto its raw video, then decides what to keep.
Turns a demo's raw recording into shareable files with ffmpeg.
Pipeable demo steps. Each takes and returns the PhoenixTest session (conn),
so they mix freely with visit/2, click_link/2, assert_has/3, etc.
Text-to-speech for PhxDemo.Steps.say/3.
Narration with the ElevenLabs text-to-speech API.
Local, free TTS with macOS's say command. Good for drafts and CI on macOS.
Narration with OpenAI's text-to-speech API.
The default narrator: Piper, a free, open-source TTS that runs locally on macOS and Linux, so it needs no API key and works in CI.
Turns demo targets into Playwright selectors.
Records what happened during a demo, and when, relative to the start of the demo setup. That is a few milliseconds after the video starts, because the browser context (and so the recording) is created first.
Auto-zoom from the cursor targets recorded by glide/3, and from the
target boxes of :upload events (upload/4, drop_files/4), so the view
holds on the file input or drop zone while the upload runs.
Mix Tasks
Records every demo (see PhxDemo.Case) and renders the videos.
Sets up phx_demo in a Phoenix app
Installs Piper into a private Python virtualenv under the user cache directory and downloads a voice.
Downloads the GeneralUser GS
soundfont (about 32 MB) into the user cache directory, so music: "x.mid"
works without a system soundfont.