superimg
Original:🇺🇸 English
Translated
SuperImg programmatic video generation framework. Create HTML/CSS video templates with defineTemplate(), animate with ctx.std (tween, math, color), and render to MP4. Use when working with superimg templates or video rendering.
18installs
Sourceanaptfox/superimg
Added on
NPX Install
npx skill4agent add anaptfox/superimg superimgTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →SuperImg Skill
How It Works
SuperImg generates videos from HTML/CSS templates. A template is a function that receives a and returns an HTML string. The renderer calls this function once per frame, advancing timing values each time.
RenderContext- (0 to 1) drives animation within a scene
sceneProgress - gives elapsed time for phase-based timing
sceneTimeSeconds - provides tween, math, and color utilities
ctx.std - carries template data (merged with defaults)
ctx.data
Templates render to MP4 via the CLI () or the server API (, ).
superimg renderrenderVideoloadTemplateTemplate Authoring
defineTemplate
typescript
import { defineTemplate } from "superimg";
export default defineTemplate({
defaults: {
title: "Hello, SuperImg!",
subtitle: "Create stunning videos from code",
accentColor: "#667eea",
},
config: {
width: 1920,
height: 1080,
fps: 30,
durationSeconds: 4,
fonts: ["Space+Grotesk:wght@400;700"],
},
render(ctx) {
const { std, sceneTimeSeconds: time, width, height, isPortrait, data } = ctx;
const { title, subtitle, accentColor } = data;
// Responsive sizing
const titleSize = isPortrait ? 64 : 88;
const subtitleSize = isPortrait ? 22 : 28;
// Phase timing: Enter 0-1.5s | Hold 1.5-3s | Exit 3-4s
const enterProgress = std.math.clamp(time / 1.5, 0, 1);
const exitProgress = std.math.clamp((time - 3.0) / 1.0, 0, 1);
// Animate title
const titleOpacity = std.tween(0, 1, enterProgress, "easeOutCubic") * (1 - exitProgress);
const titleY = std.tween(40, 0, enterProgress, "easeOutCubic");
// Staggered subtitle (+0.3s delay)
const subtitleEnter = std.math.clamp((time - 0.3) / 1.5, 0, 1);
const subtitleOpacity = std.tween(0, 0.8, subtitleEnter, "easeOutCubic") * (1 - exitProgress);
const subtitleY = std.tween(30, 0, subtitleEnter, "easeOutCubic");
// Accent line
const lineEnter = std.math.clamp((time - 0.5) / 1.0, 0, 1);
const lineWidth = std.tween(0, 100, lineEnter, "easeOutCubic") * (1 - exitProgress);
const lineColor = std.color.alpha(accentColor, 0.8 * (1 - exitProgress));
return `
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: ${width}px;
height: ${height}px;
display: flex;
align-items: center;
justify-content: center;
background: #0f0f23;
font-family: system-ui, sans-serif;
overflow: hidden;
}
.title {
font-size: ${titleSize}px;
font-weight: 700;
color: ${accentColor};
opacity: ${titleOpacity};
transform: translateY(${titleY}px);
margin: 24px 0 12px;
}
.subtitle {
font-size: ${subtitleSize}px;
color: white;
opacity: ${subtitleOpacity};
transform: translateY(${subtitleY}px);
margin-bottom: 24px;
}
.accent-line {
width: ${lineWidth}%;
max-width: 500px;
height: 2px;
background: ${lineColor};
margin: 0 auto;
}
</style>
<div style="text-align: center">
<div class="accent-line"></div>
<h1 class="title">${title}</h1>
<p class="subtitle">${subtitle}</p>
<div class="accent-line"></div>
</div>
`;
},
});RenderContext Key Fields
typescript
interface RenderContext<TData> {
std: Stdlib; // Tween, math, color utilities
// Scene timing (use these for animation)
sceneProgress: number; // 0-1 progress through current scene
sceneTimeSeconds: number; // Elapsed seconds in current scene
sceneDurationSeconds: number; // Total scene duration
// Canvas
width: number; // Canvas width in pixels
height: number; // Canvas height in pixels
isPortrait: boolean; // height > width
isLandscape: boolean; // width > height
// Data
data: TData; // Scene-specific data (merged with defaults)
// Global timing (rarely needed in templates)
globalProgress: number; // 0-1 progress through entire video
globalTimeSeconds: number;
fps: number;
}Core Stdlib
Tween (canonical animation primitive):
std.tween(from, to, progress) // Linear interpolation
std.tween(from, to, progress, "easeOutCubic") // Eased interpolation
std.tween(from, to, progress, { start, end, easing }) // Windowed animationMath:
std.math.clamp(value, min, max) // Restrict to range
std.math.map(val, inMin, inMax, outMin, outMax) // Remap rangeColor:
std.color.alpha(color, opacity) // Add transparency: alpha('#F00', 0.5)
std.color.mix(color1, color2, t) // Blend two colors
std.color.lighten(color, amount) // Lighten by percentage
std.color.darken(color, amount) // Darken by percentage
std.color.hsl(h, s, l) // Create HSL stringPhase Timing Pattern
For multi-phase animations, use with :
sceneTimeSecondsclamptypescript
render(ctx) {
const { std, sceneTimeSeconds: time } = ctx;
// Enter: 0-1s | Hold: 1-3s | Exit: 3-4s
const enterProgress = std.math.clamp(time / 1.0, 0, 1);
const exitProgress = std.math.clamp((time - 3.0) / 1.0, 0, 1);
const eased = std.tween(0, 1, enterProgress, "easeOutCubic");
const opacity = eased * (1 - exitProgress);
const y = std.tween(50, 0, enterProgress, "easeOutCubic");
// ...
}Responsive Sizing
Use / to adapt layout:
isPortraitisLandscapetypescript
const fontSize = ctx.isPortrait ? 48 : 72;
const layout = ctx.isPortrait ? "column" : "row";Rendering
CLI Rendering
bash
# Scaffold a new project (detects package manager automatically)
superimg init my-project
superimg init my-project --pm pnpm # override package manager
superimg init --add # Add videos/ to existing project
# Dev server with live preview (bare name resolves to videos/intro.ts)
superimg dev intro
# Render to video
superimg render videos/intro.ts -o output.mp4
# With options
superimg render videos/intro.ts -o output.mp4 --width 1080 --height 1920 --fps 60Programmatic Rendering (Server)
typescript
import { renderVideo, loadTemplate } from "superimg/server";
// Load template from file
const template = await loadTemplate("videos/intro.ts");
// Render to MP4
await renderVideo("videos/intro.ts", {
outputPath: "output.mp4",
width: 1920,
height: 1080,
});For low-level control, use , , and from .
createRenderPlanexecuteRenderPlanPlaywrightEnginesuperimg/serverGotchas
-
HTML strings, not JSX. Templates return template literal strings, not React/JSX. Useinterpolation for dynamic values.
${} -
Pure render function. Thefunction must be pure — no side effects, no DOM manipulation, no global state. It receives context and returns an HTML string.
render -
Use, not
sceneProgress. For animation within a scene, always useglobalProgress(0-1 within the current scene).sceneProgressspans the entire video and is rarely useful in templates.globalProgress -
Set root dimensions to canvas size. Style the root element (typicallyor an outer
body) todivso content fills the frame.width: ${width}px; height: ${height}px -
Import from the right path. Templates import from(for
"superimg", types). Server/rendering imports fromdefineTemplate."superimg/server" -
Data merges with defaults. When a template has, scene
defaultsis merged:data. You only need to pass overrides.{ ...defaults, ...data } -
Usefor Google Fonts. Declare fonts in
config.fonts(e.g.,config.fonts) instead of adding["Space+Grotesk:wght@400;700"]in your render HTML. The library automatically injects the@import url(...)tags and waits for fonts to load before capturing each frame.<link>