Wall Street-Style Explainer Video (wallstreet-explainer)
Break down a big deal: real people, real news, real footage as evidence, a graphical stage to visualize numbers and relationships; every shot picks up what the previous one left behind, so the audience always knows "what they're looking at now and why." What moves is the camera and the world, not the elements acting by themselves: the camera pushes, pulls, pans within a stage with depth layers to find the thing being talked about; the thing changes state in place. Every action matches the meaning of the sentence.
What this skill is
-
It is: a set of design principles (what to put in each shot, why, and how it justifies the next shot), a set of motion principles (measured frame by frame from three comparable reference videos: why it doesn't look like PPT or AI, a list of techniques and quantitative targets), a style language (specific ways to do stage, giant text, green-screen cutouts, discs, numbers, charts, source highlights, plus an opening hook menu), and a set of tools.
-
It is NOT a template. There are no copyable screen projects or fixed shot sequences. The visuals for each video must be conceived from the script and materials.
examples/ai-burn-money/documents how one video was conceived and where mistakes were made; don't copy its shots directly. -
Slow zooms, number scrolls, text over live footage — reference videos all use these. The techniques themselves aren't cheap; the way they're executed is. Judge with three questions: What would the audience understand less if this were removed? Is the camera moving and the object changing state, or are elements animating themselves? Is this layer real and concrete?
Before starting, read references/thinking.md and references/motion.md, then others as needed.
Workflow: Stop at each stage to show the user
The user judges the aesthetics. Don't produce the final video in one go; at each stage, show concrete things and wait for approval before moving on.
| Stage | What to do | What to show the user |
|---|---|---|
| 0 Topic selection | The topic must be both "currently hot" and "something the viewer can take away" | Two or three topics, each with a one-sentence hook |
| 1 Script | Conversational, first-person; every sentence leads to the next; insert a setup sentence where original audio will be used (references/audio.md) | Full script. Don't do formal voiceover before approval |
| 2 Director's table | See the "Director's Table" section below | Key points: how to hook the viewer, how to connect each part, whether to add setup sentences |
| 3 Materials | Official HD footage → news article source → original audio from the person → full unedited footage for green-screen cutout (no cuts, with actions matching the dialogue). First compile a contact sheet for review, then set in points, check cut points frame by frame (references/sourcing.md) | Key materials (optional) |
| 4 Voiceover & timeline | scripts/temp_voice.py: use temporary TTS to generate a timeline; all visuals are attached to words | — |
| 5 Opening | First pick 3 hooks with different mechanisms, rough-cut 5–8 seconds each for the user to choose (references/opening.md); then turn the chosen one into a sample from 0 to the first segment transition (15–20 sec, with audio) | 3 rough-cut hooks → selected opening sample |
| 6 Full video 720p | Lay out the whole video according to the director's table; after every 2-3 shots, check a still frame, and check key actions with consecutive frames (strip.py); before delivery, run motion_check.py | 720p full video, extracted frames, plus point out 2-3 places to watch carefully |
| 7 Final video | Revise until satisfied; swap to the formal voiceover; render at 1080p, only once | 1080p |
Each delivery must:
- Verify the file can be opened with
ffprobe. - The maker must have checked still frames and contact shots themselves, and checked key actions with consecutive frames.
- Place in a fixed review folder, named with a number plus Chinese description; prepend old versions with
old_. - Clearly state what changed in this version, what was measured, and what the user needs to listen to or watch themselves.
Director's Table
This is the only plan for the whole video. The user checks the visuals against it; the maker follows it during production; reviewers compare the video to it. Each shot has these columns:
| Shot | Time | Script | What's on screen, what's happening → what's left when it stops | What the audience now understands | Relies on | Comprehension moment + hold | Connection |
|---|
- What the audience now understands: a specific statement, different from the previous shot. If you can't write it, this shot should be deleted or rethought.
- Relies on: what makes this shot understandable. Person / Evidence (real material, source text) / Construction (discs, charts) / Text (big words, conclusions) / Mixed.
- Connection: what from the previous shot is picked up by this one, written as
continue / cut / carry over / zoom in / expand / chapter shiftplus that something. For the first shot, write—. - In the "on screen" column, be sure to note how the camera moves: where each movement lands (zoom to whom, pan to find what, when to pull back to see the whole picture), and which shots use green-screen cutouts.
- Script text must not be changed; one shot can cover several sentences; always stay tied to the words ("at the moment 'seven thousand' is said").
After writing, do a "read-through check": cover the "script" and "what the audience now understands" columns, read only the "on screen" column, and see if it still makes sense. Then check for multiple consecutive hard cuts and insufficiently long shots. Example at examples/ai-burn-money/director.md.
What to read
| What you're doing | Read |
|---|---|
| Deciding what to put in a shot, why, and how it connects to the next | references/thinking.md |
| Motion: why it doesn't look like PPT/AI, list of techniques, quantitative targets, camera and green-screen code feel | references/motion.md |
| What the visuals look like: stage, giant text, cutouts, discs, numbers, charts, title, subtitles, live footage frame | references/style.md |
| How shots connect, camera logic and feel | references/grammar.md |
| Opening | references/opening.md |
| Finding footage, downloading, setting in points, green-screen cutout, news sources, original audio | references/sourcing.md |
| Script, voiceover, original audio editing, sound effects, mixing | references/audio.md |
| Self-check before delivery | references/review.md |
| Common gotchas | references/lessons.md |
Tools (scripts/)
| Script | What it does |
|---|---|
footage.py | search search / get download (can download only a segment or only audio) / sheet contact sheet with timecodes / scenes scene cuts / extract extract 30fps frame sequence / wave check loudness at original audio cut points |
snap_news.mjs | Screenshot news and report sources: block ads, remove floating elements, wait for fonts to load before measuring the bounding box of keywords |
matte.py | Green-screen cutout of live footage per frame (ISNet on GPU ~0.25s/frame), flag hidden cut points, save only the person's outline area |
matte_clean.py | Post-processing for mattes: remove thin lines and fragments, erase attached chair backs based on vertical columns, write the centroid of head and shoulders per frame (for tracking the person) |
stage25d.js | Page-level utility: layer camera, directional motion blur, depth of field, green-screen person tracking, giant text, subhead, live footage sent to background, scale to nearest billion. These are functions, not shots |
temp_voice.py | script.json → temporary voiceover (edge-tts) and timeline.json (timing for each word, original audio insertion points, title positions); swaps in the formal voiceover automatically when provided; --asr to realign |
render.mjs | Per-frame rendering: --stills for stills, --video --range for a segment, --h 1080 for final video |
strip.py | Composite of consecutive frames: 8 frames per action, catch glitches invisible in stills; also used to decompose reference video actions |
motion_check.py | Camera motion self-check: total time the screen is completely static, number of still moments, time camera is moving, depth layers, push/pull ratio, compare against reference video range |
mix.py | Voiceover, original audio, sound effects, background music (music ducks for voice) → -14 LUFS; --check to measure loudness of segments |
Dependencies: ffmpeg, yt-dlp, Python (edge-tts, numpy, opencv-python, Pillow; optional faster-whisper; green-screen requires onnxruntime-gpu and the model downloaded by rembg), Node and Playwright. Installation details in README.
Voiceover: Use temporary voice first
- First use
temp_voice.pyto generate an edge-tts temporary voiceover and timeline. Build visuals against it. - Once script and visuals are finalized, swap to the formal voiceover (any TTS, voice clone, or real recording): save each line as
voice/<id>.final.wav, runtemp_voice.py script.json --asr. - Voiceover rules: always trim leading and trailing silence, maintain original speed, inter-sentence pauses are determined only by
gap. Do not speed up with atempo afterward — it sounds rushed after silence is trimmed.
How a video is structured (skeleton, not for copying)
- A video directory:
index.html,film.css,film.js,timeline.json,assets/(live footage frames inassets/clips/<name>/, green-screen mattes inassets/cut/<name>/). To use the camera utility, copyscripts/stage25d.jsinto the directory and load it beforefilm.js. film.jsexposeswindow.readyandwindow.seek(t).seekfinds the shot containing time t (viashot(t0, t1, fn)), callsfn(t)to return the HTML for that frame, and waits for image decoding and font loading. The same t always returns the same frame; do not rely on CSS animations or playback clocks.- Timings must be written as
cue(sentenceId, 'word'), taken from the timeline, not hardcoded seconds. If the voiceover changes, the entire video automatically shifts. - Live footage is extracted as frame sequences; fetch the frame by time t, don't use
<video>. Log the number of frames; missing frames cause black or empty boxes. - One shot = one camera function + several layers: camera uses
camPath(t, start, moveTable), each layer wrapped withL(camera, previousCamera, z, html); farther layers have smaller z, motion blur is calculated automatically. - To create a connection between shots, take the last frame of the previous shot's function as the opening of the next shot (by pushing through the camera, shrinking it away, expanding, or pushing it sideways). This ensures the end state of the previous shot naturally matches the start state of the next.
- Render at 720p for review; only render at 1080p once final.