Loading...
Loading...
Design, structure, and animate code-first video compositions using the gitframes WebGPU SDK. Use when creating scenes, layout trees, kinetic typography, camera trajectories, vector shapes, or audio-synchronized motion graphics in gitframes.
npx skill4agent add gatewai-dev/gitframes gitframes-composegitframesgitframesgitframes(composition, frame) → pixelsCompositionimport { Composition, Layer, FontManager } from "gitframes";
// Always register fonts before assembling layers
await FontManager.register({
family: "Inter",
source: "assets/fonts/Inter.ttf"
});
const comp = new Composition({
width: 1920,
height: 1080,
fps: 30,
durationMs: 10000, // or computeDuration()
backgroundColor: "#0E0D0C",
fonts: ["assets/fonts/Inter.ttf"],
});
comp.add(Layer.box({ /* ... */ }));
comp.add(Layer.audio("assets/score.mp3", { volume: 1 }));theme.tsBEAT = Math.round(fps * 60 / BPM)scenes/hero.tsstartFramedurationFramesframe: 0film.tscomp.add(scene())// theme.ts
export const W = 1920;
export const H = 1080;
export const FPS = 30;
export const BEAT = 18; // 100 BPM at 30 fps
export const beats = (n: number) => Math.round(n * BEAT);
export const INK = "#0E0D0C";
export const PAPER = "#EEE9E0";
export const EMBER = "#FF5A1F";
export function scene(options: { id: string; from: number; to: number; children: unknown[] }) {
return Layer.box({
id: options.id,
position: "absolute",
x: 0,
y: 0,
width: W,
height: H,
startFrame: options.from,
durationFrames: options.to - options.from,
children: options.children as never,
});
}Layer.*Layer.*AnimatableNode<T>.animate().apply()| Builder | Node | Description & Key Options |
|---|---|---|
| | Slug GPU vector text. Options: |
| | Rectangular container / card. Options: |
| | Auto-layout container (Yoga). Options: |
| | Video layer decoded via mediabunny. Options: |
| | Static raster image ( |
| | Vector SVG asset. Options: |
| | Lottie vector animation file. |
| | Audio track layer. Options: |
| | Parametric vector graphics ( |
| | SRT-driven synchronized subtitle engine. Bottom-anchored auto-wrapping cues. |
| | Charts built from native nodes (d3 scales and geometry). |
| | 3D scene camera with FOV, lens rack focus, and orbital coordinates. |
| | 3D lighting primitive ( |
| | 3D mesh loader ( |
LayerAnimationLayerAnimation.create()fromToimport { LayerAnimation } from "gitframes";
// Fluent chain
const anim = LayerAnimation.create()
.fadeIn(0, 20, "power2.out")
.fromTo("y", 60, 0, { start: 0, end: 24, ease: "expo.out" })
.fromTo("scale", 0.95, 1.0, { start: 0, end: 30, ease: "power3.out" });
// Easing bases: none, power1, power2, power3, sine, circ, expo, back, elastic, bounce, spring, cubic, hold
// Directions: in, out, inOut (e.g. "expo.out", "back.out(1.7)", "power2.inOut")keys[frame, value, ease?]at + BEAT / 2const pop = LayerAnimation.create()
.keys("scale", [[0, 0], [10, 1.12, "back.out(1.6)"], [16, 1, "power2.out"]])
.keys("opacity", [[0, 0], [6, 1]]);
// Static form: LayerAnimation.keys("rotation", [[0, -8], [20, 0, "expo.out"]])durationFramesanimation_truncatedrenderVideo({ qa: true })TextAnimatorimport { Layer, LayerAnimation, TextAnimator } from "gitframes";
// Letter-by-letter wave rise with perspective tilt and blur
const headlineNode = Layer.text("PRECISION MOTION", {
fontSize: 96,
fontFamily: "Inter",
fill: "#FFFFFF",
animators: [
TextAnimator.waveRise({
y: 40,
rotationX: 45,
opacity: 0,
blur: 10,
easing: "expo.in"
})
]
}).animate(
// Sweep the selection offset across the text string
LayerAnimation.create().kineticSweep(-1, 1, 0, 24, "power2.inOut")
);TextAnimator.waveRise()TextAnimator.wordPop()TextAnimator.blurIn()TextAnimator.flip3D()TextAnimator.colorSweep()const card = Layer.box({
width: 600,
height: 400,
background: "#181715",
borderRadius: 24,
transformStyle: "preserve-3d",
perspective: 1200,
}).animate(
LayerAnimation.create()
.fromTo("rotateX", 25, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("rotateY", -35, 0, { start: 0, end: 30, ease: "expo.out" })
.fromTo("translateZ", -200, 0, { start: 0, end: 30, ease: "expo.out" })
);CameraAnimationimport { CameraAnimation } from "gitframes";
const camAnim = CameraAnimation.camera()
.orbit({
azimuth: { from: -45, to: 15 },
elevation: { from: 10, to: 25 },
radius: { from: 2000, to: 1400 },
start: 0,
end: 90,
ease: "sine.inOut"
})
.rackFocus({
fromDistance: 2000,
toDistance: 1400,
fromFStop: 1.8,
toFStop: 4.0,
start: 20,
end: 60
});// Organic floating drift
const floatAnim = LayerAnimation.create()
.wiggle("x", { frequency: 1.5, amplitude: 25 })
.wiggle("y", { frequency: 1.2, amplitude: 20 });
// Physics spring overshoot
const springAnim = LayerAnimation.create()
.spring("scale", { damping: 14, stiffness: 190, mass: 1 });
// Audio reactivity: pass the signal object straight in…
const kick = Signal.fromArray(kickEnvelope, fps); // or Signal.builder({ type: "custom", fn })
const beatAnim = LayerAnimation.create()
.signal("scale", kick, { multiplier: 0.12, offset: 1.0, smoothing: 2 });
// …or register it once by name and bind the name (handy when many layers share it)
comp.addSignal("drum_stem_signal", kick);
const beatAnim2 = LayerAnimation.create()
.signal("opacity", "drum_stem_signal", { multiplier: 0.5, offset: 0.5 });.signal().colorSignal()value × multiplier + offsetinline_signal_Ncomp.addSignalundefinedctx.frame0startFrameyyy: 400fromTo("y", 440, 400)fromTo("y", 40, 0)borderColorborderWidthborder: "1px solid red"FontManager.register()comp.fontsxyposition: "relative"Layer.audio()durationFrames"src-alpha"