Media layers
Section titled “Media layers”Layer.video("assets/footage/clip.mp4", { width: 1920, height: 1080, fit: "cover", trimStartSec: 2, muted: true });Layer.image("assets/images/still.webp", { width: 800, height: 600, fit: "contain" }); // .png .jpg .webpLayer.svg("assets/images/logo.svg", { width: 400, height: 120, fit: "contain" });Layer.lottie("assets/lottie/badge.json", { width: 400, height: 400, fit: "contain", loop: true }); // .json .lottieLayer.caption("assets/captions/subs.srt", { /* bottom-anchored, auto-wrapping cues */ });| Builder | Notes |
|---|---|
Layer.video |
decoded with mediabunny/WebCodecs; fit, trimStartSec, muted, volume |
Layer.image |
raster only. Passing .json/.lottie/video/audio is an error (unsupported_image_format) |
Layer.svg |
vector image |
Layer.lottie |
Lottie JSON and dotLottie via ThorVG; fit: "contain" | "cover" | "fill", loop, speed, autoplay |
Layer.caption |
SRT-driven subtitles |
All of them position, animate, matte, take effects and tilt into 3D like any layer. Asset paths resolve from the working directory; build them from import.meta.url if the preview must load them too.
Media chains
Section titled “Media chains”Process a single clip outside the layer tree, e.g. to grade footage or extract a still.
import { ApplyLUT, Media } from "framefields";
const graded = Media.video("assets/footage/raw_log.mp4").apply(new ApplyLUT({ lutUrl: "assets/luts/rec709.cube" }));await graded.renderFrame({ atMs: 1500 }); // PNG Bufferawait graded.renderVideo({ outputPath: "output/graded.mp4" });Media.video, Media.image, Media.lottie, Media.audio; each has apply, renderFrame, renderFrameGrid, renderVideo.
Charts
Section titled “Charts”Layer.chart(options, boxProps) builds a chart out of ordinary boxes, paths and text — d3 does the scales and geometry, framefields draws it with your fonts.
Layer.chart( { type: "bar", // line | area | bar | scatter | candlestick | pie | donut width: 900, height: 480, categories: ["Q1", "Q2", "Q3", "Q4"], series: [ { name: "Revenue", data: [12, 19, 24, 31] }, { name: "Costs", data: [8, 11, 13, 15] }, ], yAxis: { min: 0, ticks: 4, format: "$,.0f" }, // d3-format string or (v) => string fontFamily: "Inter", fontSize: 24, textColor: "#e6e8e6", gridColor: "#2a2b2d", animate: { start: 10, duration: 30, stagger: 6, ease: "power3.out" }, // or false }, { id: "revenue", position: "absolute", x: 120, y: 200 },);| Option | Notes |
|---|---|
series: [{ name, data, color, type, curve, pointRadius }] or data |
data for pie/donut slices and candles |
stacked |
stack bars/areas |
xAxis, yAxis |
min, max, ticks, grid, format |
legend, valueLabels |
booleans |
colors, textColor, gridColor, background, cornerRadius |
style |
innerRadius |
donut hole |
upColor, downColor |
candlesticks |
animate |
built-in reveal: lines draw on, bars grow, points and slices stagger |
Children get stable ids — <id>-bar-<series>-<i>, <id>-line-<series>, <id>-slice-<i> — so you can find them in lint output. Keep scatter plots to a few hundred points: each point is a node.