mcpField
K
kinetograph

kinetograph

github.com/0xs4n4tgその他29637 回インストール今日更新
リポジトリ

kinetograph について

kinetographは、ブラウザーを使わずにRemotion形式のTypeScriptで動画を作成・レンダリングするためのスキルです。Kinetographのコマンドラインでフレームを確認し、検査してから書き出す手順を案内します。

主な機能

  1. 01Remotion APIによる動画作成
  2. 02静止画とフレーム列のプレビュー
  3. 03シーン検査とフレームのLint
  4. 04プロジェクトの型チェック
  5. 05書き出し設定による動画レンダリング

活用シーン

  1. 01TypeScriptプロジェクトから動画を作成
  2. 02レンダリング前のアニメーション確認
  3. 03レイアウト、素材、フォントの問題検出
  4. 04指定範囲のフレームをレンダリング

MCP 対応

studioを除く各コマンドに対応するMCPツールを提供します。画像コマンドは画像を直接返します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/0xs4n4tg/kinetograph --skill kinetograph

インストール後、Claude Code で/kinetographを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
AUTHORING.md
ENGINE.md
INTERNALS.md
SCENE.md

README(原文)English

プロジェクトの README より

この README の日本語訳はまだありません。以下は英語の原文です。日本語の紹介はページ上部をご覧ください。

Working with Kinetograph

Kinetograph renders video without a browser. You write TypeScript with the Remotion API and a few native elements; Node runs it; a GPU compositor draws it. You cannot watch a video, so you look at single frames and read the scene graph.

To move an existing Remotion project onto Kinetograph, use the kinetograph-migrate skill.

Run everything as npx kinetograph <command>. Add --json to get one JSON object you can parse; a failure is { "error": { "code", "message" } } with exit code 1. (Over MCP (kinetograph mcp) there is one tool per command except studio, named like the command (audio analyze is audio_analyze), with the same arguments; still, strip and onion return the image itself, and output is optional for them.)

The loop

  1. Write a project file, e.g. src/index.tsx, that calls registerRoot (see below).
  2. Look. kinetograph still src/index.tsx 45 -o out/f45.png for one frame; kinetograph strip src/index.tsx -n 12 -o out/strip.png for twelve labelled frames at once (do this first, it shows the whole animation); kinetograph onion src/index.tsx 0..60 -n 6 -o out/path.png to see a motion path. Open the PNG to look at it.
  3. Check without pixels: kinetograph inspect src/index.tsx --frame 45 gives every layer's resolved position, size, bounds, effects and text lines. kinetograph lint src/index.tsx (--frames 0,45,89 picks the frames; default 8 spread out) reports text overflowing its box or the canvas, layers off the canvas, overlapping text, missing fonts and assets, each with the frame and layer id. Fix what it says. It only checks what a frame can show: a layer at opacity 0 (or inside a group at opacity 0) and one that its clip cuts away completely are skipped at that frame, so a toast fading in off the canvas is not reported while hidden. Rendering never type-checks, so a misspelled prop (opactiy={0.5}) draws at full opacity without an error; kinetograph check src/index.tsx runs tsc --noEmit on the project (its nearest tsconfig.json) and lint reports the same errors as type-error.
  4. Fix and repeat from 2. A still takes well under a second.
  5. Render. kinetograph render src/index.tsx -o out/video.mp4. Use --draft for a half-size quick look, --range 0..90 for a part (a..b is a to b-1, a..=b includes b), --concurrency 4 to draw several frames at once on a long film (the file is the same). Export options are Remotion's: --scale 2 for 4K from 1080p (drawn at that size, not upscaled), --codec h265|prores|vp9|gif (the output extension must fit: .mov for prores, .webm for vp9, .gif for gif), --crf 20 or --video-bitrate 8M (not both), --prores-profile 4444, --pixel-format yuv444p, --audio-codec opus, --audio-bitrate 320k, and for a gif --every-nth-frame 2. Bad combinations are refused with what to change. React runs in production mode (faster); --dev-react keeps the development build when you want React's warnings.

Also: compositions <entry> lists compositions (pick one with --comp <id>; pass props with --props '{"title":"Hi"}'), audio analyze <entry> measures the sound, snapshot <entry> --update approves frames and snapshot <entry> later diffs against them, bench <entry> times the stages, effects lists effects, doctor <entry> says what the engine cannot draw and in which frames (see "Compat mode and doctor"; doctor alone says what this install can do). studio <entry> starts the local studio (preview, timeline, scene tree, props, render queue) at http://127.0.0.1:3000 and opens a browser; it is for the person at the screen, runs until stopped, and is not an MCP tool. Use --no-open when you start it yourself.

An entry is a project file (.tsx, .ts, .jsx, .js) or a .json scene (references/SCENE.md); every command takes both.

A project

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's API works as documented: useCurrentFrame, useVideoConfig, interpolate, interpolateColors, spring, Easing, random, Sequence, Series, Loop, Freeze, AbsoluteFill, Img, staticFile, delayRender. staticFile('x.png') is public/x.png next to the entry file.
  • @remotion/transitions, motion-blur, noise, paths, shapes, layout-utils, animation-utils, captions, fonts, lottie and @remotion/google-fonts/<Family> import as usual. @remotion/rive's <RemotionRiveCanvas src={staticFile('a.riv')}> plays a Rive file with Rive's own runtime (frame exact; stateMachine and stateMachineInputs props added; referenced assets beside the .riv or from assetLoader). @remotion/skia and @shopify/react-native-skia (<SkiaCanvas>, RN-Skia's components, SkSL shaders) run on real Skia (Reanimated shared values, FitBox, surfaces, Skia.Data/Image/Typeface, animated images, paragraph measurement, paint shaders, SVG text included) in the optional @kinetographdev/skia addon (npm install @kinetographdev/skia; the first Skia canvas says so if it is missing). Other @remotion/* packages do not exist yet. For new work, use the elements below. <div>s, className, stylesheets (.css imports, <style>, Tailwind) and SVG also work (compat mode, see "Compat mode and doctor"): the engine's own CSS engine styles, lays out and draws them, and a frame holding something it cannot draw yet is drawn without it (or approximated) and the command warns.
  • @remotion/sfx sounds (whip, ding, …) play with <Audio src={whip} />. The first render downloads each sound into ~/.cache/kinetograph/sfx; after that it is offline.
  • @remotion/zod-types (zColor, zMatrix, zTextarea) bundles for a <Composition schema>; Kinetograph does not check the props against the schema.
  • @remotion/timeline-utils (waveform peaks and drawing, loop and frame-strip maths) works for local files; what needs video pictures as WebCodecs frames (extractFrames) throws an UnsupportedApiError saying so.
  • @remotion/media-parser's parseMedia reads local files (staticFile('clip.mp4')) through FFmpeg: duration, dimensions, fps, codecs, tracks, rotation, sample rate, metadata, and the slow fields. Not the box tree (slowStructure) or sample callbacks (onVideoTrack); URLs fail, as everywhere (offline).
  • @remotion/webcodecs bundles; convertMedia writes a file's sound to WAV and the lists and track checks work, the rest (video encode and decode, extractFrames) throws an UnsupportedApiError. @remotion/canvas is Remotion Studio's editor canvas: it bundles, and every call throws the same error.
  • Rendering is a pure function of the frame number. Math.random(), Date.now() and timers are driven by the frame; use random(seed) for jitter. Never read the real clock.
  • Register fonts before text is laid out. Text with a font that is not registered is a lint error.

Elements and effects

Every element takes id, x, y, scale, rotation, origin="center", opacity, blendMode, effects, clip, mask, matte, motionBlur, and 3D z, rotateX, rotateY. Numbers may be plain or keyframe lists.

ElementFor
<Layer>a group; layout (flexbox) and preserve3d
<Rect>, <Ellipse>, <Path>, <Shape>vector shapes with fill (colour or gradient) and stroke
<Text>text; font, size, color, maxWidth, align, per-character chars
<Image>, <Img>PNG, JPEG, GIF, WebP, SVG files; fit="cover"
<Noise>, <MeshGradient>, <Lottie>generated and Lottie layers
<Camera>a perspective camera for 3D transforms
<Audio>a sound file: src, volume, trimBefore, playbackRate, loop

Effects (helpers of the same name; kinetograph effects lists their fields): blur, backdropBlur, glow, chromaticAberration, dropShadow, insetShadow, saturate, contrast, brightness, grayscale, sepia, invert, hueRotate, gradientMap, lut, colorGrade (HyperFrames' grade and its 18 look effects: { effects: { halftone: 0.9 } }), grain, dither, halftone, dotMatrix, displace, cornerPin, and shader for your own WGSL. motionBlur({ samples: 8 }) goes in the same effects array.

The details are in this skill's references: references/AUTHORING.md (§5 helpers, §6 elements and their JSON, §7 the Remotion API), references/SCENE.md (every layer type, effect and field; the exact meaning of each), references/ENGINE.md (the DOM engine) and references/INTERNALS.md (how a frame is made, the code map).

Sound

Web Audio code runs offline against the frame timeline. Call onFrame at module level; ctx is an OfflineAudioContext, t is the start time in seconds.

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])} /> plays a file. render mixes both into the video. Check it with kinetograph audio analyze src/index.tsx: integratedLufs (null means silence), truePeakDb (keep it under -1), clippedSamples (should be 0) and silences. Aim for about -14 LUFS for online video.

Compat mode and doctor

A project written for Remotion may use DOM elements, style, className and stylesheets next to the native ones. The engine styles and lays them out with its own CSS engine (references/ENGINE.md) and draws what it can. There is no browser: a frame holding anything it cannot draw yet is still drawn, without that item or approximated, and still, strip and render print a warning: on stderr naming each kind (element, property, values, reason, frames); with --json the same list is in the result under compat.leftOut. Pass --strict to still, strip or render to fail with UNSUPPORTED_CONTENT on the first such frame instead of getting an approximate picture. Find out what is missing before a long render. Refs to DOM elements answer getBoundingClientRect and friends from the same CSS engine, useLayoutEffect measure-then-render code settles within the frame, and fetch, new Image() and matchMedia work for project files and the composition's size, and <canvas> with getContext('2d') is drawn natively (paths, gradients, text, shadows, filters, drawImage, getImageData; <Video onVideoFrame> hands the frame to drawImage and getImageData reads its pixels), and so are webgl/webgl2 contexts; there is no network. <Canvas width height draw={(ctx) => ...} /> is the native element for the same drawing. In a component, a canvas you draw to in a frame starts that frame empty; one you don't touch keeps its pixels. Draw the whole frame from the frame number every time, never on top of what an earlier frame drew.

kinetograph doctor src/index.tsx runs the JS stage for every frame (no GPU) and reports:

  • how many frames have nothing unsupported (cleanFrames, unsupportedFrames, cleanShare);
  • each issue, grouped by element, property and reason, the one on the most frames first: values it was seen with, the element paths ($0.2 <div>, the host-tree position) and the inclusive frame ranges.

An error the page's own scripts raise and survive (console.error, an uncaught exception, an unhandled promise rejection) is an issue too, page script console.error and so on, with the message in values: a library that catches its own failure (Mermaid) draws nothing and says so only this way. --strict fails on it.

--range 0..90 limits the frames, --comp and --props choose the composition and its props. A stylesheets: note in the report says a stylesheet could not be used (Tailwind that could not be built, a remote font).

How to read it: an issue with a property is that CSS property on that element; one with null is the element itself (unsupported). 100% clean means nothing is left out. To bring the share up, remove or replace what the top issues name (a property the painter does not draw yet becomes one it does), then run doctor again. lint also lists each issue as a compat warning (it does not fail), but only for the frames it samples; doctor checks them all.

What 0.2.0 can draw and use

One line each; the pointer has the detail. kinetograph doctor alone lists the renderer and what this install can do.

  • Encoders. The built-in ffmpeg does all decoding and encodes with what it has. Any encoder it lacks runs in an external ffmpeg program: the bundled GPL one (libx264, libx265), or your own. KINETOGRAPH_FFMPEG=$(which ffmpeg) makes your system ffmpeg's encoders available, including h264_vaapi, hevc_vaapi, h264_qsv and hevc_qsv for Intel and AMD GPUs on Linux (--encoder only; never the default). H.264 defaults to libx264 off macOS hardware; VP9 on macOS and Linux; --crf, --video-bitrate. AUTHORING.md §9 (render flags), /docs/ffmpeg-license/.
  • SVG files as pictures. <Img src={staticFile('a.svg')}>, <img>, background-image, data: URLs; text inside an SVG picture is not drawn (warned). ENGINE.md "SVG files as pictures".
  • Scene colours take any CSS colour (white, rgb(), hsl(), oklch(), ...), not only hex. SCENE.md, top.
  • Missing glyphs. A character the font lacks falls back per character to bundled Noto Symbols 2, Math and Emoji (monochrome). CJK is not bundled: load a font. A character no font has is reported by doctor, --strict and inspect (missingChars). monospace is the bundled JetBrains Mono (Regular) unless you load a mono font. ENGINE.md "Fallback fonts".
  • <Series> / <TransitionSeries> accept Fragment-wrapped children, so .map() can emit pairs. AUTHORING.md §7.1.
  • renderer: line. render, still and doctor print Skia GPU (Metal) or Skia CPU (...); CPU means slow.
  • GSAP (core, free plugins, Flip): seek a paused timeline to frame / fps, or let gsap.to play by itself. AUTHORING.md §11.9, /docs/compat/#gsap.
  • Plugins @kinetographdev/gsap, p5-brush, anime, motion: seek a library with its own clock to the frame. AUTHORING.md §11.10, /docs/libraries/.
  • Libraries that render: Recharts, d3-selection, anime.js, Motion animate(), Paper.js, Bklit, rough.js, Chart.js, Plot, Vega, Shiki (code falls back to a bundled JetBrains Mono); Mermaid draws with its default HTML labels (<foreignObject>). AUTHORING.md §11.10.
  • HyperFrames projects render as they are: render|still|doctor|studio <index.html|dir>; the run downloads CDN scripts, ES modules, media and pictures first and pins them in kinetograph.lock.json (no need to run fetch-deps first; --offline never downloads; fetch-deps --update refreshes the lock). AUTHORING.md §11.11, /docs/compat/#hyperframes-projects.
  • A plain HTML page (no React) renders the same way: give the root element data-composition-id, data-width, data-height and data-duration (seconds; 30 fps), or it renders at 1920x1080 for 1 s; doctor, render and still warn when they are missing. Scripts run as in a browser; requestAnimationFrame gets each frame's time (frame 45 is 1500 ms). A page's canvas keeps its pixels from frame to frame (trails work) and a single frame replays the calls before it, so a still matches the same frame of a render.
  • Canvas. <Canvas width height draw> or <canvas> + getContext('2d'|'webgl'|'webgl2'|'webgpu'); in a component, redraw the whole frame each time (never build on earlier frames). A plain HTML page is the exception (below). AUTHORING.md §6.5 to §6.7. preserve-3d with effects is flattened (ENGINE.md).
  • <Video datamosh> smears one shot into the next with the video's motion vectors. AUTHORING.md §11.8, /docs/guides/datamosh/.
  • Motion tracking. kinetograph track public/clip.mp4 --point x,y (video pixels of the first frame, y down; find the point in a still) runs Blender's tracker once and writes clip.mp4.track.json; in a component const { x, y, rotation, scale, corners } = useTrack('clip.mp4.track.json') (from @kinetographdev/core) pins anything to it. Check the lost frame it prints; use --model locrotscale for rotation and scale. --detect finds points itself (Blender's Detect Features); --plane p1,p2,p3,p4 makes a plane track from 4+ points on a flat surface, and cornerPin(usePlaneTrack(file).pin) pins a picture to it. AUTHORING.md §11.12, /docs/guides/motion-tracking/.

Habits that save iterations

  • strip before render; lint before strip. Fix every lint error.
  • When a layer is in the wrong place, inspect it instead of guessing from the picture: bounds and matrix are in canvas pixels.
  • Give an explicit id to matte sources and to anything an <Audio> or matte refers to.
  • snapshot --update once a look is approved, so a later change that moves a pixel by more than 2 levels shows up.
  • Run doctor on a project that uses DOM elements or CSS, before render: it shows what the render will leave out or approximate.
  • A task that fails prints the cause: read error.message and error.code before changing anything.

コメント