Generate images and videos from React + Tailwind CSS templates using the loopwind CLI.
A CLI tool for generating images and videos from JSX templates using Tailwind CSS and Satori. Templates live in a .loopwind/ directory alongside your codebase.
Loopwind is a CLI tool for generating images and videos with React and Tailwind CSS. It's designed to be used with AI Agents and Cursor.
CODEBLOCK0
This installs loopwind to ~/.loopwind/ and adds the loopwind command to your PATH. Requires Node.js 18+.
Navigate to any project folder and run:
CODEBLOCK1
This creates .loopwind/loopwind.json — a configuration file with your project's theme colors.
Give your AI agent expertise in loopwind:
CODEBLOCK2
This installs a skill that teaches Claude Code (or other AI agents) how to create templates, use animation classes, and render images/videos.
With the loopwind skill installed, Claude has deep knowledge of template structure, animation classes, and Tailwind CSS patterns for Satori. Just ask:
CODEBLOCK3
CODEBLOCK4
Claude will create optimized templates and render the final output automatically.
CODEBLOCK5
Templates are installed to: INLINECODE4
Benefits:
CODEBLOCK6
or use a local props file:
CODEBLOCK7
loopwind add <source>Install a template from various sources:
CODEBLOCK8
These will be downloaded to INLINECODE6
loopwind listList all installed templates:
CODEBLOCK9
loopwind render <template> <props> [options]Render an image or video:
CODEBLOCK10
Options:
--out, -o - Output filename (default: <template>.<ext> in current directory)png, jpeg, svg (images only)loopwind validate <template>Validate a template:
CODEBLOCK11
Checks:
loopwind initInitialize loopwind in a project:
CODEBLOCK12
Creates .loopwind/loopwind.json configuration file with your project's design tokens.
Use Tailwind-style animation classes - no manual calculations needed:
CODEBLOCK13
See Animation for the complete reference.
Templates are React components that define your images and videos. They use Tailwind CSS for styling and export metadata that loopwind uses for rendering.
CODEBLOCK14
Templates are installed to .loopwind/<template-name>/.
CODEBLOCK15
CODEBLOCK16
CODEBLOCK17
CODEBLOCK18
| Format | Best For |
|---|---|
| PNG (default) | Transparency, sharp text, logos |
| JPEG |
CODEBLOCK19
CODEBLOCK20
CODEBLOCK21
| FPS | Use Case |
|---|---|
| 24 | Cinematic look, smaller files |
| 30 |
Templates receive these additional props:
frame - Current frame number (0 to totalFrames - 1)progress - Animation progress from 0 to 1CODEBLOCK22
| Encoder | Command | Use Case |
|---|---|---|
| WASM (default) | INLINECODE23 | CI/CD, no dependencies |
| FFmpeg |
loopwind render ... --ffmpeg | Faster, smaller files |
Install FFmpeg: brew install ffmpeg (macOS)
Use Tailwind-style animation classes for videos:
CODEBLOCK23
See the full Animation documentation for all classes.
CODEBLOCK24
CODEBLOCK25
image() helperLayouts let you wrap templates with consistent headers, footers, and styling. A child template specifies a layout in its meta, and the layout receives the child content as a children prop.
Create a layout template that receives children:
CODEBLOCK26
Reference the layout using a relative path:
CODEBLOCK27
CODEBLOCK28
The output uses the layout's size (1200x630) with the child content inside.
When using a layout, the layout's size controls the final output dimensions. The child template doesn't need a size property.
Use relative paths to reference layouts:
CODEBLOCK29
The layout receives:
tw, image, qr, template, etc.)frame, progress) for video layoutsCODEBLOCK30
Layouts work with video templates. Both the layout and child can use animations:
CODEBLOCK31
Create a layout for all your OG images:
CODEBLOCK32
Then create page-specific templates:
CODEBLOCK33
Use the image() helper to embed images in your templates. It supports loading from props, template directories, and URLs.
Pass the prop name to load an image path from props:
CODEBLOCK34
The image('background') helper loads from the background prop value (file path or URL).
Load images directly from your template directory by including the file extension:
CODEBLOCK35
You can also use subdirectories:
CODEBLOCK36
Template directory structure:
CODEBLOCK37
The image() helper also supports loading images from URLs:
CODEBLOCK38
.jpg, .jpeg).png).gif).webp).svg)Use Tailwind's object-fit utilities:
CODEBLOCK39
Check file paths are relative to the props file:
CODEBLOCK40
Absolute paths won't work.
Use appropriately sized images before embedding:
CODEBLOCK41
loopwind provides Tailwind-style animation classes that work with time to create smooth video animations without writing custom code.
Note: Animation classes only work with video templates and GIFs. For static images, animations will have no effect since there's no time context.
CODEBLOCK42
loopwind uses three types of animations with millisecond timing:
| Type | Format | Description |
|---|---|---|
| Enter | INLINECODE47 | Animations that play when entering |
| Exit |
exit-{type}/{start}/{duration} | Animations that play when exiting |loop-{type}/{duration} | Continuous looping animations |
All timing values are in milliseconds (1000ms = 1 second).
In addition to predefined animations, loopwind supports Tailwind utility-based animations that let you animate any transform or opacity property directly:
CODEBLOCK43
| Utility | Format | Description | Example |
|---|---|---|---|
| translate-x | INLINECODE50 | Translate horizontally | INLINECODE51 = 20px<br/>enter-translate-x-full = 100%<br/>enter-translate-x-[20px] = 20px |
| translate-y |
enter-translate-y-{value} | Translate vertically | loop-translate-y-10 = 40pxenter-translate-y-1/2 = 50%enter-translate-y-[5rem] = 80px |
| opacity | enter-opacity-{n} | Set opacity (0-100) | enter-opacity-50 = 50% |
| scale | enter-scale-{n} | Scale element (0-200) | enter-scale-100 = 1.0x |
| rotate | enter-rotate-{n} | Rotate in degrees | enter-rotate-45 = 45° |
| skew-x | enter-skew-x-{n} | Skew on X axis in degrees | enter-skew-x-12 = 12° |
| skew-y | enter-skew-y-{n} | Skew on Y axis in degrees | exit-skew-y-6 = 6° |
Translate value formats:
5 = 20px (Tailwind spacing scale: 1 unit = 4px)full = 100%1/2 = 50%, 1/3 = 33.333%, 2/3 = 66.666%, etc.[20px], [5rem], [10%] (rem converts to px: 1rem = 16px)All utilities work with:
enter-, exit-, loop-, INLINECODE79- (e.g., -translate-x-5, -rotate-45)/start/duration (e.g., enter-translate-x-5/0/800)CODEBLOCK44
CODEBLOCK45
CODEBLOCK46
CODEBLOCK47
CODEBLOCK48
You can combine multiple utility animations on the same element:
CODEBLOCK49
For more CSS-like syntax, you can use brackets with units:
CODEBLOCK50
Format: INLINECODE85
startMs - when the animation begins (milliseconds from start)When values are omitted (enter-fade-in), it uses the full video duration.
Simple opacity transitions with optional direction.
CODEBLOCK51
| Class | Description |
|---|---|
| INLINECODE89 | Fade in (opacity 0 → 1) |
| INLINECODE90 |
enter-fade-in-down/0/500 | Fade in + slide down (30px) |enter-fade-in-left/0/500 | Fade in + slide from left (30px) |enter-fade-in-right/0/500 | Fade in + slide from right (30px) |
Larger movement (100px) with fade.
CODEBLOCK52
| Class | Description |
|---|---|
| INLINECODE94 | Slide in from left (100px) |
| INLINECODE95 |
enter-slide-up/0/500 | Slide in from bottom (100px) |enter-slide-down/0/500 | Slide in from top (100px) |
Playful entrance with overshoot effect.
CODEBLOCK53
| Class | Description |
|---|---|
| INLINECODE98 | Bounce in with scale overshoot |
| INLINECODE99 |
enter-bounce-in-down/0/500 | Bounce in from above |enter-bounce-in-left/0/500 | Bounce in from left |enter-bounce-in-right/0/500 | Bounce in from right |
Size-based transitions.
CODEBLOCK54
| Class | Description |
|---|---|
| INLINECODE103 | Scale up from 50% to 100% |
| INLINECODE104 |
Rotation-based transitions.
CODEBLOCK55
| Class | Description |
|---|---|
| INLINECODE105 | Rotate in from -180° |
| INLINECODE106 |
enter-flip-in-y/0/500 | 3D flip on vertical axis |
Format: INLINECODE108
startMs - when the exit animation beginsExit animations use the same timing system but animate elements out.
CODEBLOCK56
| Class | Description |
|---|---|
| INLINECODE111 | Fade out (opacity 1 → 0) |
| INLINECODE112 |
exit-fade-out-down/2500/500 | Fade out + slide down |exit-fade-out-left/2500/500 | Fade out + slide left |exit-fade-out-right/2500/500 | Fade out + slide right |exit-slide-up/2500/500 | Slide out upward (100px) |exit-slide-down/2500/500 | Slide out downward (100px) |exit-slide-left/2500/500 | Slide out to left (100px) |exit-slide-right/2500/500 | Slide out to right (100px) |exit-scale-out/2500/500 | Scale out to 150% |exit-zoom-out/2500/500 | Zoom out to 200% |exit-rotate-out/2500/500 | Rotate out to 180° |exit-bounce-out/2500/500 | Bounce out with scale |exit-bounce-out-up/2500/500 | Bounce out upward |exit-bounce-out-down/2500/500 | Bounce out downward |exit-bounce-out-left/2500/500 | Bounce out to left |exit-bounce-out-right/2500/500 | Bounce out to right |
Format: INLINECODE128
Loop animations repeat every {durationMs} milliseconds:
/1000 = 1 second loopWhen duration is omitted (loop-bounce), it defaults to 1000ms (1 second).
CODEBLOCK57
| Class | Description |
|---|---|
| INLINECODE134 | Opacity pulse (0.5 → 1 → 0.5) |
| INLINECODE135 |
loop-spin/{ms} | Full 360° rotation |loop-ping/{ms} | Scale up + fade out (radar effect) |loop-wiggle/{ms} | Side to side wiggle |loop-float/{ms} | Gentle up and down floating |loop-pulse/{ms} | Scale pulse (1.0 → 1.05 → 1.0) |loop-shake/{ms} | Shake side to side |
Add an easing class before the animation class to control the timing curve.
CODEBLOCK58
| Class | Description | Best For |
|---|---|---|
| INLINECODE142 | Constant speed | Mechanical motion |
| INLINECODE143 |
ease-out | Fast start, slow end (default) | Enter animations |ease-in-out | Slow start and end | Subtle transitions |ease-in-cubic | Strong slow start | Dramatic exits |ease-out-cubic | Strong fast start | Impactful entrances |ease-in-out-cubic | Strong both ends | Emphasis animations |ease-in-quart | Very strong slow start | Powerful exits |ease-out-quart | Very strong fast start | Punchy entrances |ease-in-out-quart | Very strong both ends | Maximum drama |
You can apply different easing functions to enter, exit, and loop animations on the same element using enter-ease-*, exit-ease-*, and loop-ease-* classes.
CODEBLOCK59
How it works:
ease-*) applies to ALL animations if no specific override is setenter-ease-*, exit-ease-*, loop-ease-*) overrides the default for that animation typeAvailable easing classes:
| Default (all animations) | Enter only | Exit only | Loop only |
|---|---|---|---|
| INLINECODE159 | INLINECODE160 | INLINECODE161 | INLINECODE162 |
| INLINECODE163 |
enter-ease-out | exit-ease-out | loop-ease-out |ease-in-out | enter-ease-in-out | exit-ease-in-out | loop-ease-in-out |ease-in-cubic | enter-ease-in-cubic | exit-ease-in-cubic | loop-ease-in-cubic |ease-out-cubic | enter-ease-out-cubic | exit-ease-out-cubic | loop-ease-out-cubic |ease-in-out-cubic | enter-ease-in-out-cubic | exit-ease-in-out-cubic | loop-ease-in-out-cubic |ease-in-quart | enter-ease-in-quart | exit-ease-in-quart | loop-ease-in-quart |ease-out-quart | enter-ease-out-quart | exit-ease-out-quart | loop-ease-out-quart |ease-in-out-quart | enter-ease-in-out-quart | exit-ease-in-out-quart | loop-ease-in-out-quart |linear | enter-ease-linear | exit-ease-linear | loop-ease-linear |ease-spring | enter-ease-spring | exit-ease-spring | loop-ease-spring |
Spring easing creates natural, physics-based bouncy animations. Use the built-in ease-spring easing or create custom springs with configurable parameters.
CODEBLOCK60
Spring parameters:
| Parameter | Description | Effect when increased | Default |
|---|---|---|---|
| mass | Mass of the spring | Slower, more inertia | 1 |
| stiffness |
Common spring presets:
CODEBLOCK61
How spring works:
ease-spring - Uses a pre-calculated spring curve optimized for most use casesease-spring/mass/stiffness/damping - Generates a physics-based spring curve using the damped harmonic oscillator formulaease-spring, enter-ease-spring, exit-ease-spring, INLINECODE209You can use both enter and exit animations on the same element:
CODEBLOCK62
The opacities from multiple animations are multiplied together, so you get smooth transitions that combine properly.
Create sequenced animations by offsetting start times:
CODEBLOCK63
For dynamic lists, calculate the timing programmatically:
CODEBLOCK64
CODEBLOCK65
CODEBLOCK66
CODEBLOCK67
CODEBLOCK68
For complete control beyond animation classes, use progress and frame directly.
| Prop | Type | Description |
|---|---|---|
| INLINECODE212 | INLINECODE213 | 0 to 1 through the video (0% to 100%) |
| INLINECODE214 |
number | Current frame number (0, 1, 2, ... totalFrames-1) |
These are only available in video templates. Use them when animation classes aren't flexible enough.
frameCODEBLOCK69
progressCODEBLOCK70
CODEBLOCK71
Use progress/frame instead of animation classes when you need:
For everything else, prefer animation classes - they're simpler and more maintainable.
Animate elements along SVG paths with proper rotation using built-in path helpers:
CODEBLOCK72
Combine textPath helpers with animation classes to create animated text along curves:
Rotating text around a circle:
CODEBLOCK73
Animated text reveal along a path:
CODEBLOCK74
Staggered character entrance:
CODEBLOCK75
Text with bounce entrance along arc:
CODEBLOCK76
Loop animation with text on curve:
CODEBLOCK77
Tips for animating text paths:
progress for smooth rotation on circles and arcsenter-fade-in, enter-bounce-in, etc.frame for continuous effects like waves or pulsingCommon path types:
Quadratic Bezier (Q command):
CODEBLOCK78
Cubic Bezier (C command):
CODEBLOCK79
Circle:
CODEBLOCK80
Tips:
progress (0-1) for smooth animationtranslate(-50%, -50%) centers the element on the pathAnimate SVG path strokes with the stroke-dash classes, perfect for drawing or erasing line art, icons, and illustrations.
SVG stroke animations use strokeDasharray and strokeDashoffset CSS properties to create drawing effects:
All stroke-dash animations require the path length in brackets:
CODEBLOCK81
CODEBLOCK82
Draw strokes from 0% to 100%:
CODEBLOCK83
Erase strokes from 100% to 0%:
CODEBLOCK84
Continuously draw and erase:
CODEBLOCK85
To find the path length for your SVG:
CODEBLOCK86
Then use that value:
CODEBLOCK87
CODEBLOCK88
Stroke animations work alongside other animation classes:
CODEBLOCK89
For marching ants or animated dashed patterns, use frame or progress directly instead of animation classes:
CODEBLOCK90
Tips:
strokeDasharray="10 5" - 10px dash, 5px gapdashOffset * 2)This technique is different from stroke-dash classes:
stroke-dash classes - Draw/erase the stroke (reveal animation)Additional helpers for creating powerful, composable templates.
Beyond the basics, loopwind provides:
template() - Compose templates togetherFor image embedding, see the Images page.
Compose multiple templates together to create complex designs.
CODEBLOCK91
How it works:
template(name, props) renders another installed template1. Reusable components:
CODEBLOCK92
2. Complex layouts:
CODEBLOCK93
3. Dynamic content:
CODEBLOCK94
Generate QR codes dynamically in your templates.
CODEBLOCK95
Props format:
CODEBLOCK96
You can customize QR code appearance:
CODEBLOCK97
Error correction levels:
L - Low (~7% correction)Access user settings from .loopwind/loopwind.json using the config prop:
CODEBLOCK98
User's .loopwind/loopwind.json:
``json title=".loopwind/loopwind.json"
{
"colors": {
"brand": "#ff6b6b"
},
"fonts": {
"sans": ["Inter", "system-ui", "sans-serif"]
}
}
CODEBLOCK99 tsx
export default function CircleText({ tw, textPath, message }) {
return (
<div style={tw('relative w-full h-full bg-slate-900')}>
{textPath.onCircle(
message,
960, // center x
540, // center y
400, // radius
0, // rotation offset (0-1)
{
fontSize: "4xl",
fontWeight: "bold",
color: "white",
letterSpacing: 0.05
}
)}
</div>
);
}
CODEBLOCK100 tsx
// Text around a circle
textPath.onCircle("HELLO WORLD", 960, 540, 400, 0, {
fontSize: "4xl",
color: "white"
})
CODEBLOCK101 tsx
// Text along any custom path
textPath.onPath("CUSTOM PATH", (t) => ({
x: 100 + t * 800,
y: 200 + Math.sin(t * Math.PI) * 100,
angle: Math.cos(t * Math.PI) * 20
}), {
fontSize: "2xl",
fontWeight: "semibold"
})
CODEBLOCK102 tsx
// Text along a quadratic Bezier curve
textPath.onQuadratic(
"CURVED TEXT",
{ x: 200, y: 400 }, // start
{ x: 960, y: 100 }, // control point
{ x: 1720, y: 400 }, // end
{ fontSize: "3xl", color: "blue-300" }
)
CODEBLOCK103 tsx
// Text along a cubic Bezier curve
textPath.onCubic(
"S-CURVE",
{ x: 200, y: 600 }, // start
{ x: 600, y: 400 }, // control 1
{ x: 1320, y: 800 }, // control 2
{ x: 1720, y: 600 }, // end
{ fontSize: "3xl", color: "purple-300" }
)
CODEBLOCK104 tsx
// Text along a circular arc
textPath.onArc(
"ARC TEXT",
960, // center x
540, // center y
400, // radius
0, // start angle (degrees)
180, // end angle (degrees)
{ fontSize: "2xl", color: "pink-300" }
)
CODEBLOCK105 typescript
{
fontSize?: string; // Tailwind size: "xl", "2xl", "4xl", etc.
fontWeight?: string; // Tailwind weight: "bold", "semibold", etc.
color?: string; // Tailwind color: "white", "blue-500", etc.
letterSpacing?: number; // Space between characters (0-1, default: 0)
style?: any; // Additional inline styles
}
CODEBLOCK106 tsx
export default function RotatingText({ tw, textPath, progress }) {
return (
<div style={tw('relative w-full h-full bg-black')}>
{textPath.onCircle(
"SPINNING • TEXT • ",
960, 540, 400,
progress, // Rotate based on video progress
{ fontSize: "3xl", color: "yellow-300" }
)}
</div>
);
}
CODEBLOCK107 tsx
export default function MultiPath({ tw, textPath }) {
return (
<div style={tw('relative w-full h-full bg-gradient-to-br from-slate-900 to-slate-700')}>
{/* Text on outer circle */}
{textPath.onCircle(
"OUTER RING",
960, 540, 500, 0,
{ fontSize: "5xl", fontWeight: "bold", color: "white" }
)}
{/* Text on inner circle */}
{textPath.onCircle(
"inner ring",
960, 540, 300, 0.5, // offset by 50% for rotation
{ fontSize: "2xl", color: "white/60" }
)}
</div>
);
}
CODEBLOCK108 tsx
export default function PathText({ tw, textPath }) {
return (
<div style={tw('relative w-full h-full bg-gray-900')}>
{/* Draw the path */}
<svg width="1920" height="1080" style={{ position: 'absolute' }}>
<path
d="M 200 400 Q 960 150 1720 400"
stroke="rgba(255,255,255,0.2)"
strokeWidth={2}
fill="none"
/>
</svg>
{/* Text following the path */}
{textPath.onQuadratic(
"FOLLOWING THE CURVE",
{ x: 200, y: 400 },
{ x: 960, y: 150 },
{ x: 1720, y: 400 },
{ fontSize: "3xl", fontWeight: "bold", color: "blue-300" }
)}
</div>
);
}
``
For animated text paths, see Text Path Animations.
The following prop names are reserved and cannot be used in your template's
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 loopwind-1776420066 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 loopwind-1776420066 技能
skillhub install loopwind-1776420066
文件大小: 23.61 KB | 发布时间: 2026-4-17 20:05