关于 kinetograph
kinetograph 是一项技能,用于通过 Remotion 风格的 TypeScript 在无浏览器环境中制作和渲染视频。它介绍了借助 Kinetograph 命令行逐帧检查、校验和渲染项目的流程。
主要功能
- 01使用 Remotion API 制作视频
- 02预览静帧和帧条
- 03检查场景并检查帧问题
- 04对项目进行类型检查
- 05使用导出选项渲染视频
适用场景
- 01从 TypeScript 项目制作视频
- 02渲染前检查动画帧
- 03查找布局、资源或字体问题
- 04渲染指定帧范围
MCP 相关
Kinetograph 为除 studio 外的每个命令提供一个 MCP 工具;图像命令直接返回图像。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ 0xs4n4tg/ kinetograph --skill kinetograph安装后在 Claude Code 中输入/kinetograph确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/AUTHORING.mdENGINE.mdINTERNALS.mdSCENE.md详细介绍
使用 Kinetograph
Kinetograph 无需浏览器即可渲染视频。你使用 Remotion API 和少量原生元素编写 TypeScript;Node 负责运行;GPU 合成器负责绘制。你无法观看视频,因此要查看单帧并读取场景图。
要将现有 Remotion 项目迁移到 Kinetograph,请使用 kinetograph-migrate skill。
所有命令都以 npx kinetograph <command> 的形式运行。添加 --json 可获取一个可解析的 JSON 对象;失败时会返回 { "error": { "code", "message" } },退出代码为 1。(通过 MCP(kinetograph mcp)使用时,除 studio 外,每条命令对应一个工具,工具名称与命令相同(audio analyze 对应 audio_analyze),参数也相同;still、strip 和 onion 会直接返回图像本身,对这些命令来说 output 是可选的。)
工作流程
- 编写项目文件,例如
src/index.tsx,调用registerRoot(见下文)。 - 查看。 使用
kinetograph still src/index.tsx 45 -o out/f45.png获取单帧;使用kinetograph strip src/index.tsx -n 12 -o out/strip.png一次获取 12 帧带标签的画面(先做这个,它会展示整个动画);使用kinetograph onion src/index.tsx 0..60 -n 6 -o out/path.png查看运动路径。打开 PNG 查看图像。 - 检查。 无需查看像素:
kinetograph inspect src/index.tsx --frame 45会显示每个图层解析后的坐标、尺寸、边界、效果和文本行。kinetograph lint src/index.tsx(--frames 0,45,89用于指定帧;默认抽取 8 帧)会报告文本溢出文本框或画布、图层位于画布外、文本重叠、缺少字体和资源等问题,并注明帧和图层 ID。按报告修复。它只检查帧中可见的内容:在某一帧中,不透明度为 0 的图层(或不透明度为 0 的组内图层)以及被裁剪区域完全遮住的图层都会跳过,因此画布外淡入的 toast 在隐藏时不会被报告。渲染不会进行类型检查,所以拼错属性名(opactiy={0.5})会导致图像以完全不透明的状态绘制,且不会报错;kinetograph check src/index.tsx会对项目运行tsc --noEmit(使用最近的tsconfig.json),而lint会将相同错误报告为type-error。 - 修复并从第 2 步开始重复操作。生成单帧用时不到一秒。
- 渲染。 使用
kinetograph render src/index.tsx -o out/video.mp4。添加--draft可快速查看半尺寸效果;添加--range 0..90可指定片段(a..b表示从 a 到 b-1,a..=b则包含 b);长片可添加--concurrency 4同时绘制多帧(输出文件相同)。导出选项与 Remotion 相同:--scale 2可从 1080p 输出 4K(按该尺寸绘制,不会放大);--codec h265|prores|vp9|gif(输出扩展名必须匹配:prores 用.mov,vp9 用.webm,gif 用.gif);--crf 20或--video-bitrate 8M(不能同时使用);--prores-profile 4444、--pixel-format yuv444p、--audio-codec opus、--audio-bitrate 320k,GIF 还可使用--every-nth-frame 2。遇到不兼容的组合时,系统会拒绝并说明需要更改的内容。React 会在生产模式下运行(速度更快);如需 React 的警告,可使用--dev-react保留开发构建。
此外:compositions <entry> 会列出合成(使用 --comp <id> 选择一个;通过 --props '{"title":"Hi"}' 传递属性);audio analyze <entry> 会测量声音;snapshot <entry> --update 会批准帧,之后运行 snapshot <entry> 会与这些帧进行比较;bench <entry> 会统计各阶段耗时;effects 会列出效果;doctor <entry> 会指出引擎无法绘制的内容及其所在帧(参见“兼容模式和 doctor”;单独运行 doctor 会说明此安装版本支持的功能)。studio <entry> 会在 http://127.0.0.1:3000 启动本地工作室(预览、时间轴、场景树、属性、渲染队列)并打开浏览器;它面向屏幕前的操作者,会持续运行直至停止,并非 MCP 工具。自行启动时请使用 --no-open。
入口可以是项目文件(.tsx、.ts、.jsx、.js)或 .json 场景(参见 references/SCENE.md);所有命令都接受这两种入口。
一个项目
import { AbsoluteFill, Composition, Layer, Rect, Text, interpolate, registerRoot, spring,
useCurrentFrame, useVideoConfig, dropShadow, linearGradient, registerFont, staticFile } from 'remotion';
registerFont({ family: 'DM Sans', path: staticFile('DMSans-Medium.ttf') }); // fonts BEFORE any text is measured
const Card = () => {
const frame = useCurrentFrame();
const { fps, width, height } = useVideoConfig();
const s = spring({ frame, fps, config: { damping: 14 } });
return (
<AbsoluteFill style={{ backgroundColor: '#10131a' }}>
<Layer x={width / 2} y={height / 2} scale={s} origin="center" effects={[dropShadow({ y: 12, blur: 24 })]}>
<Rect width={600} height={300} radius={32} fill={linearGradient(['#f9a', '#fc6'])} />
<Text font="DM Sans" size={72} color="#fff" x={60} y={100}>gets better</Text>
</Layer>
</AbsoluteFill>
);
};
registerRoot(() => <Composition id="Main" component={Card} durationInFrames={90} fps={30} width={1080} height={1920} />);
- Remotion 的 API 按照文档运行:
useCurrentFrame、useVideoConfig、interpolate、interpolateColors、spring、Easing、random、Sequence、Series、Loop、Freeze、AbsoluteFill、Img、staticFile、delayRender。staticFile('x.png')指向入口文件旁的public/x.png。 @remotion/transitions、motion-blur、noise、paths、shapes、layout-utils、animation-utils、captions、fonts、lottie和@remotion/google-fonts/<Family>均可照常导入。@remotion/rive的<RemotionRiveCanvas src={staticFile('a.riv')}>会使用 Rive 自带的运行时播放 Rive 文件(逐帧精确;新增了stateMachine和stateMachineInputs属性;引用的资源需放在.riv文件旁,或通过assetLoader提供)。可选的@kinetographdev/skia插件(npm install @kinetographdev/skia;如果未安装,第一个 Skia 画布会提示)会让@remotion/skia和@shopify/react-native-skia(<SkiaCanvas>、RN-Skia 组件、SkSL 着色器)基于真正的 Skia 运行(包括 Reanimated 共享值、FitBox、surface、Skia.Data/Image/Typeface、动画图像、段落测量、paint 着色器和 SVG 文本)。其他@remotion/*包目前尚不存在。 新工作请使用下方的元素。<div>、className、样式表(.css导入、<style>、Tailwind)和 SVG 也能使用(兼容模式,参见“兼容模式与 doctor”):引擎自带的 CSS 引擎会为其设置样式、布局并绘制;如果某一帧包含尚无法绘制的内容,则会在不绘制该内容的情况下(或以近似方式)绘制,并由命令发出警告。@remotion/sfx的音效(whip、ding等)可通过<Audio src={whip} />播放。首次渲染时,每种音效都会下载到~/.cache/kinetograph/sfx;之后即可离线使用。@remotion/zod-types(zColor、zMatrix、zTextarea)会为<Composition schema>打包;Kinetograph 不会根据 schema 检查属性。@remotion/timeline-utils(波形峰值和绘制、循环及帧条带计算)支持本地文件;需要将视频图像作为 WebCodecs 帧的功能(extractFrames)会抛出UnsupportedApiError并说明原因。@remotion/media-parser的parseMedia会通过 FFmpeg 读取本地文件(staticFile('clip.mp4')),获取时长、尺寸、fps、编解码器、轨道、旋转角度、采样率、元数据及慢速字段。不支持 box 树(slowStructure)或采样回调(onVideoTrack);与其他地方一样,URL 会失败(离线)。@remotion/webcodecs可以打包;convertMedia可将文件的音频写入 WAV,列表和轨道检查也可使用,其余功能(视频编码和解码、extractFrames)会抛出UnsupportedApiError。@remotion/canvas是 Remotion Studio 的编辑器画布:它可以打包,但每次调用都会抛出相同的错误。- 渲染是帧编号的纯函数。
Math.random()、Date.now()和计时器均由帧驱动;抖动请使用random(seed)。切勿读取真实时钟。 - 请在排版文本之前注册字体。使用未注册字体的文本会触发 lint 错误。
元素和效果
每个元素都接受 id、x、y、scale、rotation、origin="center"、opacity、blendMode、effects、clip、
mask、matte、motionBlur,以及 3D 属性 z、rotateX、rotateY。数值可以是普通数值,也可以是关键帧列表。
| 元素 | 用途 |
|---|---|
<Layer> | 组;layout(flexbox)和 preserve3d |
<Rect>、<Ellipse>、<Path>、<Shape> | 矢量图形,带有 fill(颜色或渐变)和 stroke |
<Text> | 文本;font、size、color、maxWidth、align、逐字符设置的 chars |
<Image>、<Img> | PNG、JPEG、GIF、WebP、SVG 文件;fit="cover" |
<Noise>、<MeshGradient>、<Lottie> | 生成图层和 Lottie 图层 |
<Camera> | 用于 3D 变换的透视摄像机 |
<Audio> | 音频文件:src、volume、trimBefore、playbackRate、loop |
效果(同名辅助函数;kinetograph effects 会列出其字段):blur、backdropBlur、glow、
chromaticAberration、dropShadow、insetShadow、saturate、contrast、brightness、grayscale、sepia、invert、
hueRotate、gradientMap、lut、colorGrade(HyperFrames 的调色及其 18 种外观效果:{ effects: { halftone: 0.9 } })、grain、dither、halftone、dotMatrix、displace、cornerPin,以及用于编写自定义 WGSL 的 shader。
motionBlur({ samples: 8 }) 放在同一个 effects 数组中。
详情请参阅此技能的参考文档:references/AUTHORING.md(§5 辅助函数、§6 元素及其 JSON、§7 Remotion API)、references/SCENE.md(每种图层类型、效果和字段;各项的确切含义)、references/ENGINE.md(DOM 引擎)和 references/INTERNALS.md(帧的生成过程、代码索引)。
音频
Web Audio 代码会针对帧时间线离线运行。在模块级别调用 onFrame;ctx 是 OfflineAudioContext,t 是以秒为单位的起始时间。
import { onFrame } from '@kinetographdev/audio';
onFrame(42, (ctx, t) => {
const o = ctx.createOscillator(); const g = ctx.createGain();
o.frequency.setValueAtTime(3000, t); o.frequency.exponentialRampToValueAtTime(4500, t + 0.055);
g.gain.setValueAtTime(0.4, t); g.gain.exponentialRampToValueAtTime(0.001, t + 0.06);
o.connect(g).connect(ctx.destination); o.start(t); o.stop(t + 0.06);
});
<Audio src={staticFile('music.mp3')} volume={(f) => interpolate(f, [0, 30], [0, 1])} /> 会播放音频文件。render 会将二者混合到视频中。使用 kinetograph audio analyze src/index.tsx 检查音频:integratedLufs(null 表示静音)、truePeakDb(应低于 -1)、clippedSamples(应为 0)以及 silences。在线视频的响度目标约为 -14 LUFS。
兼容模式与 doctor
为 Remotion 编写的项目可能会在原生元素旁使用 DOM 元素、style、className 和样式表。引擎会用自己的 CSS 引擎(references/ENGINE.md)对其进行样式处理和布局,并绘制它能处理的内容。这里没有浏览器:即使某一帧包含暂时无法绘制的内容,仍会绘制该帧,只是缺少该项或以近似方式呈现;still、strip 和 render 会在 stderr 上输出 warning:,指出每种情况(元素、属性、值、原因、帧);使用 --json 时,同一列表会包含在结果的 compat.leftOut 中。为 still、strip 或 render 传入 --strict,可在遇到此类情况的第一帧时以 UNSUPPORTED_CONTENT 失败,而不是生成近似画面。长时间渲染前,先查明缺少哪些内容。对 DOM 元素的引用可通过同一 CSS 引擎提供 getBoundingClientRect 及相关方法;先测量再渲染的 useLayoutEffect 代码会在该帧内完成;fetch、new Image() 和 matchMedia 可用于项目文件及合成尺寸;<canvas> 配合 getContext('2d') 会原生绘制(路径、渐变、文本、阴影、滤镜、drawImage、getImageData;<Video onVideoFrame> 会将帧传给 drawImage,而 getImageData 可读取其像素);webgl/webgl2 上下文也同样受支持;但不支持网络。<Canvas width height draw={(ctx) => ...} /> 是用于相同绘制任务的原生元素。在组件中,如果某一帧有绘制到画布,画布会在该帧开始时清空;如果没有操作画布,则会保留其像素。每次都要根据帧编号绘制整帧,切勿在上一帧绘制的内容上继续绘制。
kinetograph doctor src/index.tsx 会对每一帧运行 JS 阶段(不使用 GPU),并报告:
- 没有不受支持内容的帧数(
cleanFrames、unsupportedFrames、cleanShare); - 按元素、属性和原因分组的每个 问题,按影响帧数从多到少排列:出现该问题时的
values、元素paths($0.2 <div>,即宿主树中的位置),以及包含首尾帧的ranges。
页面自身脚本引发且未能处理的错误(console.error、未捕获的异常、未处理的 Promise 拒绝)也会作为问题报告,例如 page script console.error,并将消息放入 values:如果某个库捕获了自身错误(Mermaid),就不会绘制任何内容,且只会通过这种方式说明原因。对此使用 --strict 会失败。
--range 0..90 可限制帧范围,--comp 和 --props 可选择合成及其属性。报告中的 stylesheets: 说明表示某个样式表无法使用(例如无法构建的 Tailwind、远程字体)。
如何解读:带有 property 的问题表示该元素的相应 CSS 属性;值为 null 则表示元素本身不受支持。100% clean 表示没有内容被遗漏。要提高这一比例,请移除或替换排名靠前的问题所指出的内容(将尚未绘制某个属性的绘制器替换为能够绘制该属性的绘制器),然后再次运行 doctor。lint 也会将每个问题列为 compat 警告(不会失败),但只检查它抽样的帧;doctor 会检查所有帧。
0.2.0 可绘制和使用的内容
每项一行;详情请查看指针。单独运行 kinetograph doctor 可列出渲染器及当前安装版本支持的功能。
- 编码器。 内置 ffmpeg 负责所有解码,并使用现有编码器进行编码。它缺少的任何编码器都会通过外部
ffmpeg程序运行:捆绑的 GPL 版本(libx264、libx265),或你自己的版本。KINETOGRAPH_FFMPEG=$(which ffmpeg)可使用系统 ffmpeg 的编码器,包括 Linux 上 Intel 和 AMD GPU 的h264_vaapi、hevc_vaapi、h264_qsv和hevc_qsv(仅限--encoder;绝不会作为默认编码器)。macOS 硬件加速不可用时,H.264 默认使用 libx264;macOS 和 Linux 上默认使用 VP9;--crf、--video-bitrate。AUTHORING.md §9(渲染标志)、/docs/ffmpeg-license/。 - SVG 文件作为图片。
<Img src={staticFile('a.svg')}>、<img>、background-image、data:URL;SVG 图片内部的文本不会绘制(会发出警告)。ENGINE.md“SVG 文件作为图片”。 - 场景颜色可使用任何 CSS 颜色(
white、rgb()、hsl()、oklch()等),不局限于十六进制颜色。SCENE.md,顶部。 - 缺失字形。 字体缺少的字符会逐个回退到捆绑的 Noto Symbols 2、Math 和 Emoji(单色)。不捆绑 CJK 字体:请加载字体。任何字体都没有的字符会由
doctor、--strict和inspect报告(missingChars)。除非你加载等宽字体,否则monospace使用捆绑的 JetBrains Mono(Regular)。ENGINE.md“回退字体”。 <Series>/<TransitionSeries>接受 Fragment 包裹的子项,因此.map()可以生成成对的元素。AUTHORING.md §7.1。renderer:行。render、still和doctor会打印Skia GPU (Metal)或Skia CPU (...);CPU 表示运行较慢。- GSAP(核心、免费插件、Flip):将暂停的时间轴定位到
frame / fps,或让gsap.to自行播放。 AUTHORING.md §11.9、/docs/compat/#gsap。 - 插件
@kinetographdev/gsap、p5-brush、anime、motion:将使用自身时钟的库定位到对应帧。 AUTHORING.md §11.10、/docs/libraries/。 - 可渲染内容的库: Recharts、d3-selection、anime.js、Motion
animate()、Paper.js、Bklit、rough.js、Chart.js、 Plot、Vega、Shiki(代码会回退到捆绑的 JetBrains Mono);Mermaid 使用其默认 HTML 标签(<foreignObject>)绘制。AUTHORING.md §11.10。 - HyperFrames 项目可直接渲染:
render|still|doctor|studio <index.html|dir>;运行时会先下载 CDN 脚本、ES 模块、媒体和图片,并将其固定在kinetograph.lock.json中(无需先运行fetch-deps;--offline绝不会下载;fetch-deps --update会刷新锁定文件)。AUTHORING.md §11.11、/docs/compat/#hyperframes-projects。 - 普通 HTML 页面(不使用 React)也能以相同方式渲染:为根元素指定
data-composition-id、data-width、data-height和data-duration(秒;30 fps),否则页面会以 1920x1080、1 秒的设置渲染;缺少这些属性时,doctor、render和still会发出警告。脚本的运行方式与浏览器相同;requestAnimationFrame会获取每一帧的时间(第 45 帧为 1500 ms)。页面的 canvas 会在帧与帧之间保留像素(可实现拖影),而单帧会重放此前的调用,因此静帧与渲染结果中的对应帧一致。 - Canvas。
<Canvas width height draw>或<canvas>+getContext('2d'|'webgl'|'webgl2'|'webgpu');在组件中, 每次都要重绘整帧(绝不要基于之前的帧继续绘制)。普通 HTML 页面例外(见下文)。AUTHORING.md §6.5 至 §6.7。带效果的preserve-3d会被扁平化(ENGINE.md)。 <Video datamosh>使用视频的运动矢量,将一个镜头涂抹到下一个镜头中。AUTHORING.md §11.8、/docs/guides/datamosh/。- 运动跟踪。
kinetograph track public/clip.mp4 --point x,y(首帧中的视频像素坐标,y 轴向下;在静帧中找到该点)会运行一次 Blender 跟踪器,并写入clip.mp4.track.json;在组件中,const { x, y, rotation, scale, corners } = useTrack('clip.mp4.track.json')(来自@kinetographdev/core)可将任何内容固定到该点。检查它打印的lost帧;使用--model locrotscale可跟踪旋转和缩放。--detect会自动查找点(Blender 的 Detect Features);--plane p1,p2,p3,p4会根据平坦表面上的 4 个或更多点创建平面跟踪,cornerPin(usePlaneTrack(file).pin)则可将图片固定到该平面。AUTHORING.md §11.12、/docs/guides/motion-tracking/。
减少迭代次数的习惯
- 先运行
strip再运行render;先运行lint再运行strip。修复所有 lint 错误。 - 图层位置不对时,使用
inspect检查,不要凭图片猜测:bounds和matrix的单位是画布像素。 - 为遮罩源以及
<Audio>或遮罩引用的任何对象指定明确的id。 - 样式获批后运行一次
snapshot --update,这样之后任何导致像素变化超过 2 级的改动都会显示出来。 - 对使用 DOM 元素或 CSS 的项目,在运行
render之前运行doctor:它会显示渲染时会省略或近似处理的内容。 - 失败的任务会打印原因:进行任何更改之前,先查看
error.message和error.code。