成片制作管线总谱与卡点 19 种风格提示词关键代码 踩坑清单复现

CODE → FRAME → BEAT

用代码,做一支
61 秒的视频。

19 种视觉风格、1830 帧画面、120 BPM 配乐。 没有提示词生成模型,也没有素材库——每一帧都由 HTML / Canvas / SVG / Three.js 逐帧渲染, 每一秒音乐都由 numpy 从零合成。

看 61 秒全片

1920 × 108030 fpsH.264 + AAC0 个视频生成模型

成片画面 · 在抖音观看
61.0s / 1830 帧 / 26.9MB 抖音观看 ↗
PALETTE #d97757 · CORAL120 BPM · A MINOR
61.0s
成片时长
帧级精确到 61.000 秒
1830帧
渲染帧数
26 段逐段 ffprobe 合计
19种
视觉风格
11 段展示 + 8 连闪
120BPM
卡点节拍
1 拍 = 0.5s,音画偏移 0.00ms
0个
视频生成模型
画面与音乐全部代码合成
26.9MB
成片体积
2-pass 3.4Mbps · H.264+AAC

01 / THE FILMS

一支成片,一套方法

全片按 120 BPM 剪辑表生成。

成片画面 · 在抖音观看
FULL FILM · 61s抖音观看 ↗

61 秒全片

61.000s / 1830f

开场打字 → 11 轮风格展示(每轮 1 秒大字卡 + 3 秒展示)→「还有更多?」→ 八连闪 → 电影感落版。30fps、1830 帧、2-pass 3.4Mbps,音画互相关偏移 0.00ms。

1830 帧19 风格3.4 Mbps26.9 MB

视频为网页演示用 720p 转码;原始成片 1920×1080。

02 / PIPELINE

四步管线,全部可复现

核心原则只有一条:画面是时间的纯函数。给定 t,渲染结果永远一致,所以可以逐帧拆开、并行重渲、精确对拍。

01

HTML分镜即页面

每个片段一个 HTML,暴露 window.DURATION 与 window.renderFrame(t)。没有 rAF、没有 Date.now、没有 Math.random——随机数走带种子的 mulberry32,同一帧可以无限次复现。卡点冲击(白闪 0.85→0 / 缩放 1.13→1 / 抖动 14px / 推镜 1→1.05)由 common.js 统一注入,风格段左上角自动生成胶囊标签。

window.renderFrame = DSH.installSegment(scene, { duration: 3, shock: true, label: { num:'01', name:'扁平插画 · 科普' } })
02

PLAYWRIGHT逐帧渲染

无头 Chromium 打开页面,evaluate(renderFrame(i/30)) 之后经 CDP 截 JPEG(q95),直接通过 image2pipe 管道交给 ffmpeg 编码 libx264 crf16——不落盘、不丢帧。WebGL 页面固定 SwiftShader 软渲染并开启 preserveDrawingBuffer,保证每帧都能截到。

await page.evaluate(t => window.renderFrame(t), i / 30) // → CDP captureScreenshot(jpeg, 95) → ffmpeg
03

NUMPY合成配乐

120 BPM、A 小调、不采样。kick、snare、clap、808、超级锯齿、古筝、笛子等 15 类音色全部用 numpy/scipy 合成;每段风格配专属乐器。底鼓包络驱动 55% 侧链闪避,2.2 秒合成混响,tanh 软削波,最终归一化到 -0.6 dBFS。

python audio/synth.py # → out/audio.wav · 61.000s · 48kHz stereo · -0.6dBFS
04

FFMPEG总装交付

26 段按剪辑表 concat,2-pass 3.4Mbps 输出 H.264 + AAC。每段 render 后 ffprobe 核对帧数,合计必须是 1830;成片音轨与母带互相关校验偏移 0.00ms;全片抽 1fps 接触表复核结构。

node scripts/build.mjs --concat # concat × 2-pass 3.4Mbps + AAC → 61.000s / 26.9MB

03 / SCORE

总谱与卡点

120 BPM → 1 拍 0.5 秒。11 轮展示里,每段 第 1 帧就是“啪”的卡点,之后每 0.5 秒一个动作;配乐与画面共用同一组时间常数。

开场 8s
11 轮 × 4s
52–53
闪
落版
0.0s8.0s52.0s53.0s55.0s61.0s
开场:打字 → 代码流 → 粒子爆炸 11 轮:大字卡 1s + 风格展示 3s 大字卡「还有更多?」 八连闪 2s 电影感落版
01
想做 科普视频?
扁平插画 · 科普t0 0.35×1.4
02
想做 知识讲解?
数学动画t0 0.5×1.45
03
想开 产品发布会?
极简发布会t0 0×1.0
04
想做 培训课件?
手绘白板t0 0.3×1.4
05
想做 炫酷预告片?
赛博故障t0 0.8×1.3
06
想拍 国风短片?
水墨国风t0 0.5×1.35
07
想做 数据新闻?
数据可视化t0 0.4×1.4
08
想做 游戏视频?
8-bit 像素t0 0.9×1.25
09
想录 编程教程?
代码录屏t0 0.4×1.4
10
想要 3D质感?
Three.js 三维t0 0.3×1.3
11
想做 竖屏短视频?
竖屏短视频t0 0.3×1.45

t0 / speed:片段模式把片段时间 u 映射为场景时间 t = t0 + u × speed。
配乐卡点:每轮第 1 拍 kick+tom,第 2 拍 kick+clap+sub boom+whoosh;展示段首帧 6 层大卡点(kick+clap+snare+crash+sub boom+高频爆裂)。

04 / STYLES

19 种风格,逐帧手写

11 段 3 秒展示 + 8 个 0.25 秒闪现。每一张都是渲染管线直接输出的帧,点击可放大看细节。

05 / THE PROMPT

原始提示词,162 行

规格、剪辑表、11 段分镜、配乐设计、质量检查——一份可直接交给代码 Agent 的任务书。

这是本片最初的任务提示词,完整保留。它不描述“感觉”,而是把能数字化的部分全部钉死:1920×1080 / 30fps / 1830 帧 / 120BPM、每段 t0 与 speed、每个风格的技术要点、配器与和弦、以及一条条质量检查。

原文品牌为 Opus 5.5;本项目 v2 已整体替换为 DeepSeek V4.1-Flash,其余规格与分镜完全一致。

01先锁死规格:帧数、时长、BPM、分辨率。所有后续取舍都围绕这几个数字。
02把「禁止项」写进提示词:本片硬约束是「0 个视频生成模型」,逼出纯代码方案。
03音画共用同一组时间常数:卡点表既驱动画面也驱动配乐,最后用互相关校验偏移。

复制后可直接开一个新会话,把整份提示词交给代码 Agent;建议一次只做 2–3 个片段,先出接触表再整段渲染。

prompt.txt
原提示词 · 162 行CODE-VIDEO / TASK BRIEF

06 / CODE

关键代码,三小段

卡点封装、逐帧渲染、配乐侧链——工程里最关键的三个函数,原样摘录。

applyFrame() — 白闪 / 缩放 / 抖动 / 推镜
// common.js —— 片段模式:u 是本片时间,t = t0 + u * speed
function applyFrame(u) {
  const t = t0 + u * speed;
  wrap(t, u);

  // 卡点冲击:白闪 0.85→0(0.12s) / 缩放 1.13→1(0.32s outCubic) / 抖动 14px(0.16s)
  if (o.shock && u < 0.45) {
    const f = Math.max(0, 1 - u / 0.12);
    flash.style.opacity = (0.85 * f * f).toFixed(4);
    sc *= lerp(1.13, 1, ease.outCubic(clamp(u / 0.32)));
    const shake = 14 * Math.max(0, 1 - u / 0.16);
  }

  // 之后整段缓慢推镜 1 → 1.05
  if (o.push) sc *= 1 + 0.05 * ease.inOutSine(clamp(u / duration));
  stage.style.transform = `translate3d(${dx}px,${dy}px,0) scale(${sc})`;
}
render.mjs — evaluate → CDP → image2pipe
// 逐帧:不落盘,JPEG 直接推进 ffmpeg stdin
for (let i = 0; i < frames; i++) {
  const t = i / fps;
  await page.evaluate((tt) => window.renderFrame(tt), t);

  // 等两帧合成,确保 CDP 截到刷新后的 DOM / canvas
  await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r))));

  const shot = await cdp.send('Page.captureScreenshot', { format: 'jpeg', quality: 95 });
  const buf = Buffer.from(shot.data, 'base64');
  if (!ff.stdin.write(buf)) await new Promise((r) => ff.stdin.once('drain', r));
}
synth.py — kick 侧链(闪避 55%)
# 底鼓:正弦 120→45 Hz 扫频 + 高频 click + tanh
def synth_kick(f0=120.0, f1=45.0, dur=0.5, tau=0.13):
    n = smp(dur); t = tvec(n)
    f = f1 + (f0 - f1) * np.exp(-t / 0.030)
    ph = 2.0 * np.pi * np.cumsum(f) / SR
    body = np.sin(ph) * xenv_ad(n, 0.0015, tau)
    click = hp(RNG.standard_normal(n), 2500) * np.exp(-t / 0.0035) * 0.30
    return fade_tail(norm_peak(np.tanh(1.6 * (body + click)), 0.98), 8)

# 侧链总线:每个 kick 触发 55% 闪避,严格整数样本
def build_sidechain():
    duck = np.zeros(N)
    for s, w in DUCKS:
        m = min(env_len, N - s)
        cur = duck[s:s + m]
        np.maximum(cur, base[:m] * w, out=cur)
    return 1.0 - 0.55 * np.clip(duck, 0.0, 1.0)

07 / PITFALLS

踩坑清单,八条

这些坑都真实发生过——记下来是为了下一次不用再踩。

01

静态 FFmpeg 下载不完整

某镜像下到 12.5MB 就断,解压报 Unexpected end of input。解法:改用系统包,或 pip 的 imageio-ffmpeg;视频管线只用 stdin/stdout,不依赖额外构建。

02

字体下载慢到超时

raw.githubusercontent 只有 ~90KB/s,一个 6MB 字体要一分多钟。解法:换 jsDelivr 镜像(gcore 节点实测 ~1MB/s),并只下载真正用到的字重。

03

每帧 ctx.filter blur 让软渲染变慢十倍

模糊/发光一旦每帧实时计算,单帧从 80ms 涨到 400ms+。解法:预渲染成离屏 canvas 贴图,逐帧只做 drawImage 与交叉淡化。

04

接触表第一格不是第 0 帧

ffmpeg fps=2 采样会取到 u≈0.23s,看不到首帧白闪。解法:用 select='eq(n,0)+eq(n,15)…' 精确取帧,或单帧 -ss 抽帧核对。

05

Playwright 缺系统库

WSL 里 Chromium 报 libnss3 / libnspr4 not found。解法:装系统依赖;WebGL 页面另加 --use-angle=swiftshader 与 preserveDrawingBuffer。

06

并行渲染同一片段写坏文件

两个进程同时写同一个 .part.mp4,faststart 重定位失败,产物只有 2 帧。解法:一个片段同时只允许一个渲染进程;失败先删 .part 再重跑。

07

AAC 封装有约 13ms 填充

成片音轨比母带多 640 个样本,肉眼听不出但会累积。解法:用互相关对成片音轨与母带做偏移校验,本片实测 0.00ms。

08

61 秒必须等于 1830 帧

任何一段少一帧,concat 后整条时间轴都会错。解法:每段 render 后 ffprobe -count_frames 核对,26 段合计必须精确 1830。

08 / REPRODUCE

从零复现,六步

Node + Playwright + FFmpeg + Python(numpy/scipy) 即可完整重建;本页所有画面都来自这套工程。

01

准备环境

ffmpeg -version · node -v · python -m venv .venv && pip install numpy scipy
02

下载字体与依赖

node ci: playwright install chromium · 字体从 google/fonts 镜像下载
03

先渲染单段验证构图

node scripts/render.mjs --page pages/s01.html --out out/s01.mp4 --dur 3 --t0 0.35 --speed 1.4
04

全量渲染 26 段

node scripts/build.mjs --all --force --jobs=7
05

合成配乐

python audio/synth.py # 61.000s / 48kHz / -0.6dBFS
06

总装 + 2-pass 压缩

node scripts/build.mjs --concat # → 61.000s / 1830 帧 / 26.9MB
project/
├── common.js            # 缓动 / 种子随机 / 片段封装 / 胶囊标签
├── pages/               # 26 个片段页面
│   ├── intro.html  end.html
│   ├── card.html   flash.html
│   └── s01.html … s11.html
├── scripts/
│   ├── render.mjs        # Playwright → CDP → ffmpeg
│   └── build.mjs         # 渲染 / concat / 2-pass
├── audio/
│   └── synth.py          # 61s 配乐(numpy/scipy)
├── assets/
│   ├── fonts/            # 11 个开源字体
│   └── vendor/           # three.js / KaTeX / RoomEnvironment
└── out/                # 分段 / 接触表 / 成片 / 音频

“以上每一帧,
都是一行一行代码写出来的。”

19 种风格 · 1830 帧 · 0 个视频生成模型

再看一遍 61 秒
制作 · DSH Agent Teams(8 个角色并行)| 画面 HTML / Canvas / SVG / Three.js + Playwright 逐帧
音乐 · numpy / scipy 从零合成,120 BPM,A 小调| 合成 · FFmpeg 2-pass 3.4Mbps
本站 · 单页静态站点 · 部署于 Workbench CLI → nginx 子目录
原始成片 1920×1080;网页内为 720p 演示转码 · #d97757