Video is a function. Give it a frame number, get back HTML.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Template │ │ HTML │ │ Frames │ │ MP4 │
│ │ ──▶ │ │ ──▶ │ │ ──▶ │ │
│ render(ctx) │ │ <div>...</ │ │ ░░▓▓██████ │ │ video.mp4 │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
f(time) string rasterize encode
No timeline editor. No After Effects. Just TypeScript that returns HTML strings.
Timeline editors are built for one video at a time. Drag keyframes, tweak curves, export, upload, repeat. Try to batch render 200 product videos and you're fighting the tool.
SuperImg treats video like any other code problem:
- Deterministic — Same input, same output. Every frame is testable.
- Composable — Import functions, reuse components, version control everything.
- Scalable — Render one video or ten thousand. Same template, different data.
npm install superimgimport { defineTemplate } from 'superimg'
export default defineTemplate({
config: { width: 1920, height: 1080, fps: 30, durationSeconds: 5 },
render(ctx) {
const { std, sceneProgress, width, height } = ctx
// sceneProgress runs from 0 → 1 over the duration
const scale = std.tween(0.8, 1, sceneProgress, 'easeOutCubic')
return `
<div style="
width: ${width}px;
height: ${height}px;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
">
<h1 style="
font-size: 80px;
color: white;
transform: scale(${scale});
">Hello, SuperImg</h1>
</div>
`
},
})Render it:
npx superimg render template.ts -o video.mp4 ┌─────────────────┐
│ template.ts │
│ │
│ defineTemplate │
└────────┬────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │ │ CLI │ │ React │
│ │ │ │ │ │
│ Live edit │ │ npx render │ │ <Player/> │
│ 60fps │ │ Batch jobs │ │ Embed │
└─────────────┘ └─────────────┘ └─────────────┘
CLI — Render locally or in CI. Batch render from JSON data:
npx superimg render template.ts --data products.json -o ./output/Browser — Live preview at 60fps while you edit.
React — Embed anywhere with <Player/>:
import { Player } from 'superimg-react'
import template from './template'
<Player template={template} width={1280} height={720} />Every frame receives a context object. No magic globals.
render(ctx) {
const {
width, height, // Dimensions
sceneProgress, // 0 → 1 over duration
sceneFrame, // Current frame number
sceneTimeSeconds, // Current time in seconds
std, // Standard library
data, // Your custom data
} = ctx
return `<div>...</div>`
}Everything you need for animation, available on ctx.std:
| Module | What it does |
|---|---|
std.math |
clamp, map, inverseLerp, mapClamp |
std.color |
hexToRgb, rgbToHsl, mix, alpha |
std.tween |
tween(from, to, progress, easing?) — canonical animation primitive |
std.timing |
createPhaseManager, sequence — segment helpers for multi-scene |
std.css |
css, fill, center, stack — style helpers |
std.presets |
Platform dimensions: YouTube, Instagram, TikTok |
await render(template, { format: 'youtube.video' }) // 1920×1080
await render(template, { format: 'youtube.video.short' }) // 1080×1920
await render(template, { format: 'instagram.post' }) // 1080×1080
await render(template, { format: 'tiktok.video' }) // 1080×1920Pass data at render time for personalization:
export default defineTemplate({
defaults: {
productName: 'Widget',
price: '$99',
},
render(ctx) {
const { data } = ctx
return `<div>${data.productName} - ${data.price}</div>`
},
})npx superimg render template.ts --data '{"productName": "Gadget", "price": "$149"}'| Package | Description |
|---|---|
superimg |
Core library + CLI |
superimg-react |
React <Player/> component |
npm install superimg # Core + CLI
npm install superimg-react # React components- API Reference — RenderContext and stdlib
- Templates & Data — Creating templates with defaults
- Examples — Working examples to copy from
Templates execute code. Treat them as trusted input, or run rendering in a sandbox.
MIT