superimg
SuperImg Skill
Mental Model
Video is a pure function of time. Your template's render(ctx) is called once per frame. It receives a RenderContext and returns an HTML string. All animation is derived from sceneProgress (0→1 over the scene) or sceneTimeSeconds (elapsed seconds). Use std.tween as the canonical animation primitive. Data comes from data, merged with per-scene overrides. No timeline editor, no keyframes — just math and HTML.
Quick Start
import { defineScene } from "superimg";
export default defineScene({
data: { message: "Hello!", accentColor: "#667eea" },
config: {
duration: 3,
inlineCss: ["* { margin: 0; box-sizing: border-box; } body { background: #0f0f23; font-family: system-ui; }"],
},
render(ctx) {
const { std, sceneProgress, width, height, data } = ctx;
const { style } = std.motion.enter(sceneProgress, { y: 30 });
return `
<div style="${std.css({ width, height }, std.css.center())}">
<div style="${std.css({ color: data.accentColor, fontSize: 64 }, style)}">${data.message}</div>
</div>
`;
},
});
Key Context Fields
Use these from ctx: sceneProgress, sceneTimeSeconds, sceneDurationSeconds, width, height, isPortrait, data, std, asset(). Ignore globalProgress for scene animation — use sceneProgress or sceneTimeSeconds instead.
Co-located assets (zero config): ctx.asset('logo.png') returns a URL for assets/logo.png next to your .video.ts file. For named assets with preloaded metadata, use config.assets + ctx.assets.
Core Patterns
Motion helpers (recommended for fade+slide animations):
// Single-phase enter animation
const { style } = std.motion.enter(sceneProgress, { y: 30 });
// Enter-hold-exit animation
const { style } = std.motion.enterExit(sceneProgress, { y: 40, enterEnd: 0.2, exitStart: 0.8 });
Phase splitting (for multi-phase timing):
const { enter, hold, exit } = std.phases(sceneProgress, { enter: 1, hold: 2, exit: 1 });
const { style } = std.motion.enter(enter.progress, { y: 30 });
Responsive sizing:
const r = std.createResponsive(ctx);
const fontSize = r({ portrait: 48, square: 32, default: 40 });
Timeline API (for complex choreography):
const tl = std.timeline(time, duration);
const enter = tl.at("enter", 0, 0.8);
const scale = enter.active ? std.tween(0.8, 1, enter.progress, "easeOutBack") : 1;
Animated counter: Math.floor(std.tween(0, value, progress, "easeOutCubic"))
Layout: std.css({ width, height }), std.css.center(), std.css.fill(), std.css.stack()
Color: std.color.alpha(color, 0.5), std.color.mix(c1, c2, t)
Stdlib Cheat Sheet
std.motion.enter(progress, { y: 20 })— fade in + slide (returns{ style, opacity, transform })std.motion.enterExit(progress, opts)— three-phase enter/hold/exit animationstd.phases(progress, { enter: 1, hold: 2, exit: 1 })— split progress into named phasesstd.createResponsive(ctx)— factory forr({ portrait: X, default: Y })std.tween(from, to, progress, "easeOutCubic")— low-level eased interpolationstd.math.clamp,std.math.map— value clamping and mappingstd.color.alpha,std.color.mix— color manipulationstd.css(obj)— object → inline style stringstd.timeline(time, duration)— declarative timeline for complex choreographystd.spring(progress, config?)— spring curve 0→1 with overshoot/bouncestd.springTween(from, to, progress, config?)— spring-interpolated valuestd.createSpring(config?)— create spring easing for use withstd.tween()std.stagger(items, progress, opts?)— distribute progress across items with cascading delaysstd.interpolate(progress, inputRange, outputRange)— multi-keyframe interpolationstd.interpolateColor(progress, inputRange, colors)— multi-keyframe color interpolation
Do / Don't
DO: Use sceneProgress or sceneTimeSeconds for animation. Put shared CSS in config.inlineCss. Use config.fonts for Google Fonts. Set root element to width: ${width}px; height: ${height}px. Use std.css() for inline styles. Import from "superimg" in templates.
DON'T: Return JSX — return template literal strings. Mutate state in render — keep it pure. Use globalProgress for scene animation. Import from "superimg/server" in templates. Use @import url() for fonts — use config.fonts. Override CLI output paths (e.g. using -o) when rendering unless explicitly instructed by the user.
Config
defineScene config: width, height, fps, duration, fonts, inlineCss, stylesheets, outputs. Precedence: CLI flags > template config > _config.ts (cascading) > built-in defaults. Use defineConfig in _config.ts for project-wide settings.
CLI
Note for AI Agents: Do not use the
-oor--outputflag when rendering unless the user explicitly requests a custom path. Rely on the framework to determine the output location automatically.
superimg init my-project
superimg init . # Add to existing project
superimg dev intro
superimg render videos/intro.ts # Outputs natively to nearest package's output/intro.mp4
superimg render videos/intro.ts -o custom.mp4
superimg list
superimg info videos/intro.ts
superimg setup
superimg add skill
Server API
import { renderVideo, loadTemplate } from "superimg/server";
const t = await loadTemplate("videos/intro.ts");
await renderVideo("videos/intro.ts", { outputPath: "out.mp4", width: 1920, height: 1080 });
Additional Resources
For detailed API documentation and working examples, consult:
Reference Files
- references/api.md — Complete RenderContext interface, all std.tween easings, full std.math/color/css/timing APIs, config options
Example Files
- examples/hello-world.ts — Minimal template demonstrating core concepts
- examples/stats-card.ts — Advanced template with phase timing, animated counters, responsive sizing
Project Examples
See examples/templates/ in the SuperImg repo for full templates: lower-thirds, stats-card, phase-demo, countdown, spring-stagger-demo.