最近在 GitHub 上看到了一个挺有意思的项目——HyperFrames(heygen-com/hyperframes,Apache-2.0 协议,21,219 Stars)。
简单说,它就是一个 HTML → 视频的渲染器。你用网页技术写动画,它给你输出 MP4。
为什么值得写?最近 AI 视频赛道挺热闹的,Runway、Pika、Minimax 都在卷。但我更感兴趣的是生产级工具——能流水线跑的那种。HyperFrames 解决了这个问题:不管你有多少条视频要出,只要模板固定,AI 帮你批量生成。
正好最近在研究 pi(earendil-works/pi,56,679 Stars,MIT 协议),它和 HyperFrames 配合起来很有意思——pi 写代码,HyperFrames 出视频,整个链路都是 AI 驱动。
今天分享一下这个组合怎么用。
先说清楚它们的定位:
pi 是终端编程 harness,写 HTML/CSS/JS 能力的,但本身不输出视频。
HyperFrames 是渲染器,接收 HTML 文件,输出 MP4/WebM。
这两者之间没有原生集成——pi 写完 HTML,人把文件复制到 HyperFrames 项目目录里,跑 CLI 命令。所以流程是这样的:
用户(描述需求) → pi(写 HTML,含 HyperFrames 标记)
↓
HyperFrames CLI(lint → inspect → render)
↓
MP4 视频输出
核心连接点就是剪贴板——pi 输出代码,你复制进 HyperFrames 项目目录。
npx hyperframes init pi-agent-video --example product-promo --non-interactive
这条命令做了几件事:
- 创建项目目录结构(index.html + compositions/ 文件夹)
- 复制示例资源到 assets/
- 安装依赖
项目长这样:
pi-agent-video/
├── index.html # 主时间线(场景调度)
├── compositions/ # 各场景组件
├── assets/ # 资源文件
├── package.json
└── meta.json
主时间线文件负责把各个场景组件按时间顺序拼起来,类似视频编辑器里的序列轨道。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Pi Agent - AI 编程助手</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #0a0a0f;
font-family: "Inter", sans-serif;
}
.scene { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1920" data-height="1080">
<!-- 背景音乐 -->
<audio id="bg-music" src="E:/hermes-agent/share-video/static/music.mp3" data-start="0" data-volume="0.3" data-media-start="0"></audio>
<!-- 场景 1: Logo 开场 (0-2秒) -->
<div data-composition-id="scene1-logo-intro" data-composition-src="compositions/scene1-logo-intro.html" data-start="0" data-duration="2" data-track-index="1" data-width="1920" data-height="1080"></div>
<!-- 场景 2: 什么是 Pi (2-6秒) -->
<div data-composition-id="scene2-what-is-pi" data-composition-src="compositions/scene2-what-is-pi.html" data-start="2" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
<!-- 场景 3: 核心能力 (6-11秒) -->
<div data-composition-id="scene3-core-features" data-composition-src="compositions/scene3-core-features.html" data-start="6" data-duration="5" data-track-index="1" data-width="1920" data-height="1080"></div>
<!-- 场景 4: 技能展示 (11-16秒) -->
<div data-composition-id="scene4-skills-tools" data-composition-src="compositions/scene4-skills-tools.html" data-start="11" data-duration="5" data-track-index="1" data-width="1920" data-height="1080"></div>
<!-- 场景 5: 结尾 (16-20秒) -->
<div data-composition-id="scene5-outro" data-composition-src="compositions/scene5-outro.html" data-start="16" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
</div>
<script>
window.__timelines = window.__timelines || {};
const mainTl = gsap.timeline({ paused: true });
window.__timelines["main"] = mainTl;
</script>
</body>
</html>
关键参数:
- data-composition-id — 每个场景的唯一标识(必须是有效 UUID 或字符串,不能留空)
- data-start — 在主时间线上的起始时间(秒)
- data-duration — 持续时长(秒)
场景组件是独立的 HTML 文件,放在 compositions/ 目录下。每个场景有自己的动画时间线。
场景 1:Logo 开场
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Pi Logo 开场</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 50%, #16213e 100%);
font-family: "Inter", sans-serif;
}
#scene1-logo-intro {
width: 1920px;
height: 1080px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.logo-container { display: flex; flex-direction: column; align-items: center; gap: 20px; }
.glow-ring {
position: absolute;
width: 400px;
height: 400px;
border: 2px solid rgba(108, 99, 255, 0.3);
border-radius: 50%;
}
.pi-symbol {
font-size: 180px;
font-weight: 700;
color: #fff;
text-shadow: 0 0 80px rgba(108, 99, 255, 0.6);
}
.pi-wordmark { font-size: 72px; font-weight: 700; color: #fff; letter-spacing: 4px; }
.tagline {
font-size: 28px;
font-weight: 400;
color: rgba(255, 255, 255, 0.7);
letter-spacing: 8px;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="scene1-logo-intro" data-composition-id="scene1-logo-intro" data-width="1920" data-height="1080">
<div class="glow-ring"></div>
<div class="logo-container">
<div class="pi-symbol">π</div>
<div class="pi-wordmark">Pi Agent</div>
<div class="tagline">你的 AI 编程助手</div>
</div>
</div>
<script>
(function () {
const tl = gsap.timeline({ paused: true });
// 光环动画:从小到大淡出
tl.fromTo(".glow-ring", { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.8, ease: "power2.out" }, 0.2);
tl.to(".glow-ring", { scale: 1.8, opacity: 0, duration: 0.6, ease: "power2.in" }, 1.0);
// Pi 符号弹入
tl.fromTo(".pi-symbol", { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.6, ease: "back.out(1.7)" }, 0.4);
// 文字滑入
tl.fromTo(".pi-wordmark", { y: 30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.5, ease: "power2.out" }, 0.8);
tl.fromTo(".tagline", { y: 20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.5, ease: "power2.out" }, 1.1);
// 淡出结束
tl.to(".logo-container", { opacity: 0, duration: 0.4, ease: "power2.in" }, 1.6);
window.__timelines = window.__timelines || {};
window.__timelines["scene1-logo-intro"] = tl;
})();
</script>
</body>
</html>
场景 2:产品定位
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>什么是 Pi</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
margin: 0;
width: 1920px;
height: 1080px;
overflow: hidden;
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 50%, #16213e 100%);
font-family: "Inter", sans-serif;
}
#scene2-what-is-pi {
width: 1920px;
height: 1080px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.content { text-align: center; max-width: 1200px; }
.headline {
font-size: 80px;
font-weight: 700;
color: #fff;
line-height: 1.2;
margin-bottom: 40px;
}
.highlight { color: #6c63ff; }
.description {
font-size: 32px;
font-weight: 400;
color: rgba(255, 255, 255, 0.7);
line-height: 1.6;
max-width: 900px;
margin: 0 auto;
}
.particles { position: absolute; width: 100%; height: 100%; pointer-events: none; }
.particle {
position: absolute;
width: 6px;
height: 6px;
background: rgba(108, 99, 255, 0.5);
border-radius: 50%;
}
</style>
</head>
<body>
<div id="scene2-what-is-pi" data-composition-id="scene2-what-is-pi" data-width="1920" data-height="1080">
<div class="particles">
<div class="particle" style="top: 15%; left: 20%;"></div>
<div class="particle" style="top: 25%; left: 75%;"></div>
<div class="particle" style="top: 60%; left: 15%;"></div>
<div class="particle" style="top: 70%; left: 85%;"></div>
<div class="particle" style="top: 40%; left: 30%;"></div>
<div class="particle" style="top: 80%; left: 60%;"></div>
<div class="particle" style="top: 35%; left: 50%;"></div>
<div class="particle" style="top: 55%; left: 70%;"></div>
</div>
<div class="content">
<h1 class="headline">AI 协作的<br/><span class="highlight">新时代</span></h1>
<p class="description">Pi 是你的私人 AI 编程助手,实时理解上下文、执行任务,<br/>从你的工作流程中不断学习进化。</p>
</div>
</div>
<script>
(function () {
const tl = gsap.timeline({ paused: true });
// 标题滑入
tl.fromTo(".headline", { y: 50, opacity: 0 }, { y: 0, opacity: 1, duration: 0.8, ease: "power3.out" }, 0);
// 描述文字滑入
tl.fromTo(".description", { y: 30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6, ease: "power3.out" }, 0.5);
// 粒子渐入 + 漂浮动画
tl.to(".particle", { opacity: 1, duration: 0.4, stagger: 0.1 }, 0.2);
tl.to(".particle", { y: -15, duration: 2.5, ease: "sine.inOut", stagger: 0.4 }, 0.5);
// 场景结束淡出
tl.to(".content", { opacity: 0, duration: 0.4 }, 3.6);
window.__timelines = window.__timelines || {};
window.__timelines["scene2-what-is-pi"] = tl;
})();
</script>
</body>
</html>
其他三个场景(核心能力、技能展示、结尾 CTA)用类似结构编写,完整代码就不在这里展开了。
写完组件之后,跑两条命令检查有没有问题:
# 语法和结构检查
npx hyperframes lint
# 布局溢出检查(文字有没有跑到框外)
npx hyperframes inspect
lint 会告诉你缺少什么属性、语法错误在哪;inspect 会渲染一遍时间线,报告布局问题。
都没问题的话,预览一下:
npx hyperframes preview --port 3002
这条命令会启动本地服务,给你一个 Studio URL,在浏览器里可以看到视频预览效果,带时间轴和播放控制。
最后,渲染输出 MP4:
npx hyperframes render --output "E:/pi-agent/hyperframes-video/pi-agent-intro.mp4" --quality high
输出参数说明:
| 参数 | 选项 | 默认值 |
|---|---|---|
--output |
文件路径 | renders/name_timestamp.mp4 |
--quality |
draft / standard / high |
standard |
--fps |
24 / 30 / 60 |
30 |
--format |
mp4 / webm |
mp4 |
data-composition-id 必须有效如果属性值不合法(空字符串、格式错误的 UUID),lint 会报错但不说清楚原因。
修复方法:用标准 UUID 格式,比如 scene1-logo-intro(字符串也行,关键是不能为空)。
transform 和 GSAP 混用会冲突/* 错误:GSAP 会覆盖 CSS transform */
.pi-symbol { transform: scale(0); }
/* 正确:用 fromTo 从初始状态开始动画 */
tl.fromTo(".pi-symbol", { scale: 0, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.6 }, 0.4);
记住规则:动画元素的初始状态交给 GSAP 管理,不要写在 CSS 里。
背景音乐要用 <audio src="..." data-start="0"> 的形式,直接把 src 写在元素上,不要用子元素 <source>。否则 lint 会报 media_missing_src 错误。
| 适合 | 不适合 |
|---|---|
| 批量视频生产(相同模板,不同内容) | 单条需要逐帧精修的视频 |
| AI 驱动的自动化流水线 | 一次性的创意视频(用 AE/Pr 更顺手) |
| 有明确时间线的结构化内容 | 复杂嵌套时间线、互相依赖的动画 |
总结一下:如果你有大量同类型的视频要出(产品介绍、教程合集、品牌宣传片),这条链路值得跑通。pi 负责写代码,HyperFrames 负责渲染,人只需要描述需求和最终质检,中间环节全自动化。
AI 工具的价值不在于单个有多强,而在于能不能串成一条高效的工作流。pi + HyperFrames 这个组合,恰好说明了这一点。
工具本身没有壁垒——会用的人多了,壁垒就变成了用得熟练的程度。
你平时有跑通过什么 AI 工作流?是怎么串起来的?评论区聊聊。
Pi + HyperFrames 这个组合上手真的快
原来还能这样用 AI 做视频
我之前用 Remotion,比这个麻烦多了
pi 写 HTML + HyperFrames 出视频,这条链路确实顺
💬 评论区