1仕組みと動作原理
映像と音声の同源性:まずStrudelトラックをオフラインで音声としてレンダリングし、各ノートイベントをそれを生成したコードトークンにマッピングします。動画はこれらのイベントによって駆動されるため、後処理の位置合わせではなく、各ハイライトが正確に自身のノートと一致します。
The music is rendered offline from the code, and the video is driven by Strudel's own note events, so every highlight lands on its note.
録音パイプライン:record.mjs が127.0.0.1上でrecorder.htmlとnode_modulesのみを配信する静的サーバーを起動し、ミュート状態のヘッドレスChromeを使用してパートごとに個別評価を行います。各ノートのタイムスタンプ、所属パート、値、コード文字範囲をエクスポートし、トラック全体のミックス(オプションのエンディングを含む)と._scope()パートのソロステムをオフラインでレンダリングします。
// Serves recorder.html (the pinned @strudel/web build from node_modules) on 127.0.0.1, opens it in a … // muted headless Chrome, and for the track named in <project>/video.json: … // 1. evaluates every labelled part on its own and exports each note event with its time, part,
オフライン一貫性の鍵:recorder.html はローカルのnode_modulesから固定バージョンの@strudel/webのみを読み込み、strudel.ccからは何も取得しません。サンプルトラックはサンプラーを使用しない純粋なシンセサイザーであるため、初回の実行で追加のサンプルネットワークリクエストは不要です。
nothing is fetched from strudel.cc. Samples are only fetched if the track itself calls samples(...).
タイムラインは自動生成:build-data.mjs がStrudelのcycleを動画の秒数に換算し、window.SQ(トークンハイライト、セクション起動、ウィジェット、エネルギー曲線)を生成します。総再生時間をindex.htmlのdata-durationに書き戻し、音声の開始点をpreとして設定します。
const sec = (c) => +(PRE + c * SPC).toFixed(4); // strudel cycle -> video seconds … fs.writeFileSync(path.join(project, "data/events.js"), "window.SQ = " + JSON.stringify(out) + ";\n"); … // stamp the length into index.html; the audio stays the length of the recording
カメラワークもデフォルトで自動計画:カメラ指定がない場合、セクションの起動に合わせたフレーミング、build時のall( ブロックへのズーム、drop後の新セクション公開、終了時のサインオフへのズームアウトを自動で行います。上書きが必要な場合のみvideo.jsonにcamera行を記述します。
Leave `camera` out to get the automatic plan: frame each part as it powers on, push in on the `all(` block during builds, reveal the newest part after a drop, pull wide after the last drop, and zoom to the sign-off.
録音品質は事後対処ではなくゲートキーパー:録音中は4小節ごとにオーディオレベルを出力して警告し、dropのピークがクリッピングなしで≤ -2 dBFSになるまで再録音を要求します。楽曲を変更した場合はステップ5〜7に戻って再録音し、video.jsonの変更のみであればステップ6〜7に戻るだけです。
Fix the track and record again until the drops peak at or below -2 dBFS with no clipped samples.
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| cli | HyperFrames CLI(钉版 0.8.70,经 npx --yes 调用;check/snapshot/render/cloud render) |
| package | @strudel/web 1.3.0(AGPL-3.0,从 npm 装,不进仓库) |
| package | puppeteer-core 25.12.0 + @puppeteer/browsers 3.2.3 与 OFL 字体(@fontsource/jetbrains-mono、rubik-mono-one) |
| package | Chrome for Testing 149.0.7827.22(录音用;可用 CHROME=<path> 覆盖) |
| network | 成片构图在载入时从 jsDelivr 取 GSAP 3.14.2(故渲染并非完全离线) |
| api | HyperFrames 云渲染(可选,消耗用户账户额度,须用户显式同意才可走) |
| network | 轨道自身若调用 samples(...),录音时会去轨道写明的采样 URL 取音频(示例轨道无采样) |
4推奨統制措置と判定根拠
- scripts/: ローカルスクリプトまたはコマンドの実行: node scripts/new-project.mjs <dir>(完全ローカルのファイルコピー:テンプレート + トラック + 4つのtoolsファイル)
- 网络端点: 外部ネットワークアクセスの宣言: npm install(npmレジストリ。tools/package.jsonでバージョン固定)
- 本地工作目录: ファイル読み書きおよび成果物出力: <project-dir>内のみに限定: data/events.json, data/events.js, assets/bgm.wav, assets/st
5二重目の独立検証
- [ok] 『No credentials.』声明是否成立 — SKILL.md Rights/Side effects 段明写 'No credentials.';目录内 process.env 命中只有 record.mjs 的 CHROME 与 CFG 类入参,未见 key/token/cookie/keychain 读取,也无 .env 解析。
- [discrepancy] 『录音离线』是否等于全流程离线 — 录音段确实离线(127.0.0.1 静态服务 + 本地无头 Chrome + node_modules 里的 @strudel/web;recorder.html 注释明写 nothing is fetched from strudel.cc),但建仓(npm install、Chrome for Testing 下载)与渲染期构图载入 GSAP(cdn.jsdelivr.net)都要联网。SKILL.md 自己把这些写进 Side effects,故非隐瞒;只是『离线』的适用范围仅限录音。据此把档位定为蓝(官方源固定版本安装 + 产物期 CDN,与同 repo 先例 hyperframes-animation / hyperframes-core 同口径)。
- [ok] 高亮是否真由音符事件驱动(而非后期对齐) — 链路可读通:record.mjs 导出每音符的 locs(代码字符区间)→ build-data.mjs 用 byLoc 汇总成 token 时间表与段落/widget 时间 → 写成 window.SQ → 模板 app.js 消费。音画同源成立,不是后期对轨。
- [discrepancy] 是否误把 ffmpeg 列为依赖 — 本 skill 目录内不调用 ffmpeg:录音与 mix 走 Strudel 的离线渲染器,编码由 HyperFrames CLI 负责。故 external_deps 不列 ffmpeg(同批姊妹 skill 有列,本 skill 没有)。
- [ok] 大写 S 标签的拒绝逻辑是否真在代码里 — record.mjs 实有 'const soloing = labels.filter((l) => l.length > 1 && l.startsWith("S"));' 并在命中时 fail 提示改名——不是文档空话。
- [unlocatable] 录制耗时与云渲染额度的具体数值 — 『约 5-7 分钟/32 小节』与云渲染『消耗多少额度』只有文档声明,源码内无可复现依据;本次为只读侦查,未运行录音或渲染,故不作为已核事实使用。
6結論
90eabb744789abe6…