psychopomp
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePsychopomp explainer reels
Psychopomp 讲解短片
An explainer reel is several independently authored Scene Plans joined on one
clock: behavior stories on the GPU Stage (orbs, cards, beams, packets, camera,
bloom), pinned Callouts, Rolling Numbers, changes as animated Stepped Diffs,
captions in a terminal voice, and optional spoken narration. When narrated,
every visual beat waits for a phrase in the transcript, so re-voicing re-times
the film automatically.
The working examples are in : (#50825 as a
Stage film that zooms into its code), (#50042 combining Stage,
Callouts, Rolling Number, and a Zoom into an annotated code diff), and
(the five-PR reel with sequence diagrams). Copy their shape; do not start from a
blank crate. Component payloads, channels, and commands live in
("Make A Narrated Explainer Reel") and terms in ; read both before
authoring. Engineering rules are in .
scenes/pr-walkthroughsrc/flagship.rssrc/stop_stage.rssrc/lib.rsSCENE_PLANS.mdCONTEXT.mdAGENTS.md一个讲解短片由多个独立编写的 Scene Plans 在同一时钟上衔接而成:在 GPU Stage 上呈现行为演示(球体、卡片、光束、数据包、摄像机、辉光),固定 Callouts 和 Rolling Numbers,以动画 Stepped Diffs 呈现变更,加入终端风格的字幕,并可选用口头旁白。加入旁白后,每个视觉节拍都会等待转录文本中的一个短语,因此更换配音后,影片会自动重新计时。
可运行的示例位于 :(#50825,一部缩放到其代码的 Stage 影片)、(#50042,将 Stage、Callouts、Rolling Number 和 Zoom 组合成带注释的代码 diff),以及 (包含五个 PR 和时序图的短片)。复制它们的结构;不要从空白 crate 开始。component payload、channel 和 command 位于 ("Make A Narrated Explainer Reel"),术语位于 ;创作前务必阅读这两份文档。工程规则位于 。
scenes/pr-walkthroughsrc/flagship.rssrc/stop_stage.rssrc/lib.rsSCENE_PLANS.mdCONTEXT.mdAGENTS.mdSteps
Steps
-
Establish the facts. For each change, read the actual diff and PR description (,
gh pr diff) and write one sentence each for the broken behavior, the fixed behavior, and the change. Done when every claim you will narrate is traceable to code, a test, or a reproduced run; mark anything you could not verify and keep it out of the script.gh pr view -
Write the script as, one clip per part: intro, then per change
scenes/<name>/narration/script.json,<slug>-before,<slug>-after, then outro. Follow STORY.md when writing it. Done when each clip is under about 30 seconds spoken and every planned visual beat has a distinct anchor phrase in it.<slug>-code -
Voice it (optional). Draft first with no credentials (uses macOS
--draft), or synthesize final narration with ElevenLabs (say, default modelengine: "elevenlabs") or Fish Audio (eleven_v4):engine: "fish"json{ "engine": "elevenlabs", "model": "eleven_v4", "voice": "<elevenlabs-voice-id>", "settings": { "stability": 0.5, "similarity": 0.7 }, "clips": [ { "id": "change-before", "text": "[warm, conversational voice] ..." } ] }sh# Draft (no API key required) bun scripts/narrate.ts scenes/<name>/narration/script.json --draft # ElevenLabs (requires ELEVENLABS_API_KEY and script.voice) ELEVENLABS_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.json # Fish Audio (requires FISH_AUDIO_API_KEY) FISH_AUDIO_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.jsonnormalizes loudness, transcribes word timings with Whisper, and writesscripts/narrate.ts,<id>.mp3, and<id>.words.json. When switching voices or engines, rebuild the Scene Plan against the new word timings.narration.jsonOr declare the narration in the Scene Program with(SCENE_PLANS.md, "Declare Narration And Sound"):psychopomp-media,media.say(id, &voice, text),media.dialogue, andmedia.sfx, thenaudio.derive(Effect::pitch(..)). Each line is generated once and recorded inmedia.finish()?; later runs call nothing unless a declaration changed, and ElevenLabs words come from its character alignment with the script's spelling. Check the cost first withmedia.lock.json(zero API calls), time withPSYCHOPOMP_MEDIA=plan cargo run -p <crate>, and clean up withPSYCHOPOMP_MEDIA=draft. Move a narrate.ts scene withPSYCHOPOMP_MEDIA=prune(no regeneration). Done when a plain run reports every resourcecargo run -p psychopomp-media -- adopt scenes/<name>/narration.= -
Author the Scene Program in(the workspace picks it up; add it to
scenes/<name>with a few key times). Useverify.json(psychopomp::score, "Author With The Score DSL") soSCENE_PLANS.mdis the onlyPlanBuilderbinding and every actor (mut,Stage,Camera,Caption,Callout,RollingNumber,Checklist,Meter,Bars,Subtitles,Confetti,Terminal,Chat,ChangedFiles,LowerThird) is an immutable value whose methods return composableLenss (Beat,.then,.also,.with,.on_end,.after,.early,stagger,each). Schedule clips withat!(scene, time => ...), build elements withNarration::readingand relativeStageElement::card|orb|beam|packet|label|ringenvelopes onpsychopomp::layout::Placement, frame withStagePost::RESTRAINED, and play sounds withCaption::header|chip|footer(orsfx::*.beat(id, gain_db)foraudio.beat(gain_db)). Load thepsychopomp-mediaskill and apply it to every beat: cardsexplainer-motion, beamssettle_in, messagesconnect, replies wait forsend, impacts.reply(), the herohit, the fix'sorb_in; userewindto pin annotations to Stage elements or Editor code ranges andCalloutfor live counters/timers; drive camera moves throughRollingNumber(stage.camera(),establish,frame,follow,release,orbit,whip). Reach for the visualization and text overlays instead of hand-building them from Stage labels and rings:dolly_zoomfor checks that run and resolve,Checklist(Meter) for a timeout ring,MeterPlan::countdownfor before/after numbers,Barsfor a success beat, andConfetti(Subtitles) to burn in word-timed captions. Changes areSubtitlesPlan::from_spokens ofDiff,keep, andadd(step)lines, each step keyed to a phrase. Done whenremove(step)writes the reel andcargo run -p <crate>reports valid. A missing phrase panics with its clip: change the anchor to words the transcript actually contains.cargo run --release -- plan validate <reel> -
Review before rendering. Get segment spans from, then contact-sheet each segment at its before, switch, after, and code beats:
plan inspect <reel>shbun scripts/sheet.ts <reel> t1,t2,... --theme neutral --shutter --out output/sheet.jpgWhen tuning the look, setand editPSYCHOPOMP_SHADER_DIR=crates/psychopomp-render/src/render/stage.wgsl: every frame and sheet picks up shader edits without a Rust rebuild. Then render one behavior segment with audio (stage_post.wgsl) or a 2–3 second window (plan render <reel> out.mp4 --cue <scene-id>) and inspect frames extracted during motion. Done when every segment has been looked at and no text overlaps, clips, or reads against the wrong chip.--range a..b -
Render and verify the whole reel once, after the sheets and short windows pass. Run it in the foreground with a long timeout and never end a turn while it is still running:sh
cargo run --release -- plan render <reel> output/<name>.mp4 --theme neutralDone whenshows 1920x1080, 60 fps, AAC audio, and a duration matchingffprobe, and loudness is near -16 LUFS with peaks under -1 dBFS.plan inspect -
Deliver the MP4 path. Commit the scene, narration assets, and any engine changes in Psychopomp with conventional messages.
-
确定事实。 针对每项变更,阅读实际的 diff 和 PR description(、
gh pr diff),分别用一句话说明故障行为、修复后的行为以及变更内容。完成标准是:你将要旁白陈述的每一项说法都可追溯到代码、测试或复现运行;对于任何无法验证的内容,应予以标记并排除在脚本之外。gh pr view -
编写脚本,保存为,每个部分对应一个 clip:先使用
scenes/<name>/narration/script.json,然后针对每项变更依次使用intro、<slug>-before、<slug>-after,最后使用 outro。编写时遵循 STORY.md。完成标准是:每个 clip 的朗读时长约不超过 30 秒,并且每个计划中的视觉节拍都有一段独特的 anchor phrase。<slug>-code -
配音(可选)。 先在没有凭据的情况下生成草稿(使用 macOS
--draft),或使用 ElevenLabs(say,默认模型engine: "elevenlabs")或 Fish Audio(eleven_v4)合成最终旁白:engine: "fish"json{ "engine": "elevenlabs", "model": "eleven_v4", "voice": "<elevenlabs-voice-id>", "settings": { "stability": 0.5, "similarity": 0.7 }, "clips": [ { "id": "change-before", "text": "[warm, conversational voice] ..." } ] }sh# Draft (no API key required) bun scripts/narrate.ts scenes/<name>/narration/script.json --draft # ElevenLabs (requires ELEVENLABS_API_KEY and script.voice) ELEVENLABS_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.json # Fish Audio (requires FISH_AUDIO_API_KEY) FISH_AUDIO_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.json会标准化响度,使用 Whisper 转录单词时序,并写入scripts/narrate.ts、<id>.mp3和<id>.words.json。更换声音或引擎时,需要根据新的单词时序重建 Scene Plan。narration.json或者在 Scene Program 中声明旁白,使用(SCENE_PLANS.md,"Declare Narration And Sound"):psychopomp-media、media.say(id, &voice, text)、media.dialogue和media.sfx,然后调用audio.derive(Effect::pitch(..))。每条内容只生成一次并记录在media.finish()?中;除非声明发生变化,后续运行不会执行任何调用,ElevenLabs 的单词时序来自其 character alignment,并与脚本中的拼写对应。先使用media.lock.json检查成本(零 API 调用),使用PSYCHOPOMP_MEDIA=plan cargo run -p <crate>计时,再使用PSYCHOPOMP_MEDIA=draft清理。使用PSYCHOPOMP_MEDIA=prune迁移 narrate.ts scene(不重新生成)。完成标准:普通运行会报告每个资源均为cargo run -p psychopomp-media -- adopt scenes/<name>/narration。= -
在中编写 Scene Program(workspace 会自动识别;将其加入
scenes/<name>,并附上几个关键时刻)。使用verify.json(SCENE_PLANS.md,"Author With The Score DSL"),使psychopomp::score成为唯一带PlanBuilder的绑定,并且每个 actor(mut、Stage、Camera、Caption、Callout、RollingNumber、Checklist、Meter、Bars、Subtitles、Confetti、Terminal、Chat、ChangedFiles、LowerThird)都是不可变值,其方法返回可组合的 Beat(Lens、.then、.also、.with、.on_end、.after、.early、stagger、each)。使用at!(scene, time => ...)安排 clip,使用Narration::reading构建元素,在StageElement::card|orb|beam|packet|label|ring上应用相对StagePost::RESTRAINEDenvelope,使用psychopomp::layout::Placement设置画面框架,并通过Caption::header|chip|footer播放声音(或对sfx::*.beat(id, gain_db)使用psychopomp-media)。audio.beat(gain_db)加载skill,并将其应用于每个 beat:卡片使用explainer-motion,光束使用settle_in,消息使用connect,回复等待send,冲击使用.reply(),主角使用hit,修复使用orb_in;使用rewind将标注固定到 Stage element 或 Editor code range,使用Callout显示实时计数器或计时器;通过RollingNumber驱动摄像机移动(stage.camera()、establish、frame、follow、release、orbit、whip)。应优先使用可视化和文字叠加层,而不是通过 Stage label 和 ring 手工构建它们:使用dolly_zoom呈现执行并完成的检查项,使用Checklist(Meter)呈现超时环,使用MeterPlan::countdown呈现前后数字,使用Bars呈现成功节拍,使用Confetti(Subtitles)烧录按词计时的字幕。变更由SubtitlesPlan::from_spoken表示,其中包含Diff、keep和add(step)行,每个 step 都以短语为键。完成标准:remove(step)写出 reel,且cargo run -p <crate>报告有效。如果缺少短语,程序会连同其 clip 触发 panic:请把 anchor 改为转录文本中确实包含的词。cargo run --release -- plan validate <reel> -
渲染前审查。 从获取 segment span,然后在每个 segment 的 before、switch、after 和 code beat 处,为各 segment 生成 contact sheet:
plan inspect <reel>shbun scripts/sheet.ts <reel> t1,t2,... --theme neutral --shutter --out output/sheet.jpg调整外观时,设置并编辑PSYCHOPOMP_SHADER_DIR=crates/psychopomp-render/src/render/stage.wgsl:每一帧和 sheet 都会应用 shader 修改,无需重新构建 Rust。stage_post.wgsl然后渲染一个带音频的 behavior segment(),或渲染一个 2–3 秒窗口(plan render <reel> out.mp4 --cue <scene-id>),并检查在运动过程中提取的帧。完成标准:每个 segment 都已检查过,没有文字重叠或被裁切,也不会落在错误的 chip 上。--range a..b -
完整渲染并验证一次整个 reel,且仅在 contact sheet 和短窗口全部通过后进行。使用较长的 timeout 在前台运行,且仍在运行期间绝不能结束当前 turn:sh
cargo run --release -- plan render <reel> output/<name>.mp4 --theme neutral完成标准:显示 1920x1080、60 fps、AAC 音频,时长与ffprobe一致;响度接近 -16 LUFS,峰值低于 -1 dBFS。plan inspect -
交付 MP4 路径。 使用符合惯例的 commit message,将 scene、旁白素材以及对 Psychopomp 所做的引擎变更提交到版本库。
Improving Psychopomp
改进 Psychopomp
When a reel needs something the engine cannot do, add it to Psychopomp rather than
working around it in one scene: plan types and validation in ,
strict-channel preflight in , pixels in , GPU-free tests plus
an GPU test, and the docs asks you to keep current. Run
before the change and after it to prove every other showroom and film is
untouched.
crates/psychopompplan_runtimerender#[ignore]AGENTS.mdcargo run --release -- verify baselineverify compare --expect <scenes you changed>Build from small reusable pieces. Interpolation, easing, curves, shape ports, and
connectors belong in (organized like pmndrs : core /
/, , , , ; glam vectors). Extend it
instead of inlining math in a renderer or scene, and split renderers into one small
helper per element, as does. Keep
, ,
and green.
psychopomp::mathmathlerpremapsmoothstepeasingcurveshapesrandomrender/stage.rscargo fmt --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace如果某个 reel 需要引擎尚不支持的功能,应将其添加到 Psychopomp,而不是在单个 scene 中绕过:在 中添加 plan 类型和验证,在 中添加 strict-channel preflight,在 中添加 pixels,添加无需 GPU 的测试和一个带 的 GPU 测试,并按照 的要求保持文档为最新状态。变更前运行 ,变更后运行 ,以证明其他每个 showroom 和 film 均未受影响。
crates/psychopompplan_runtimerender#[ignore]AGENTS.mdcargo run --release -- verify baselineverify compare --expect <scenes you changed>应使用小型可复用组件进行构建。插值、缓动、曲线、形状端口和连接器应归入 (其组织方式类似 pmndrs :核心 //、、、、;glam vectors)。应扩展该模块,而不是在 renderer 或 scene 中内联数学逻辑;并将 renderer 拆分为每个 element 一个小型 helper,正如 所做。保持 、 和 全部通过。
psychopomp::mathmathlerpremapsmoothstepeasingcurveshapesrandomrender/stage.rscargo fmt --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace