loopwind 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,435 @@
1
+ <!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="generator" content="Astro v4.16.19"><link rel="canonical" href="https://loopwind.dev/sdk/"><!-- Primary Meta Tags --><title></title><meta name="title"><meta name="description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta name="keywords" content="loopwind, image generation, video generation, React, Tailwind CSS, Satori, CLI tool, templates, shadcn/ui, AI agents, Cursor, automation"><meta name="author" content="loopwind"><!-- Open Graph / Facebook --><meta property="og:type" content="website"><meta property="og:url" content="https://loopwind.dev/sdk/"><meta property="og:title"><meta property="og:description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta property="og:image" content="https://loopwind.dev/og-image.png"><meta property="og:site_name" content="loopwind"><!-- Twitter --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://loopwind.dev/sdk/"><meta name="twitter:title"><meta name="twitter:description" content="CLI-based Image &#38; Video Generator for developers and AI Agents. Generate stunning visuals from React + Tailwind templates."><meta name="twitter:image" content="https://loopwind.dev/og-image.png"><!-- Theme Color --><meta name="theme-color" content="#0a0a0a"><!-- Additional SEO --><meta name="robots" content="index, follow"><meta name="language" content="English"><link rel="stylesheet" href="/_astro/agents.I1-fN38o.css"></head> <body class="antialiased"> <div class="min-h-screen" data-astro-cid-mw7aashj> <!-- Sidebar --> <aside class="fixed top-0 left-0 h-screen w-64 border-r border-border bg-card overflow-y-auto z-10" data-astro-cid-mw7aashj> <div class="p-6" data-astro-cid-mw7aashj> <a href="/" class="block mb-8 no-underline" data-astro-cid-mw7aashj> <h1 class="text-3xl flex items-center gap-2 bg-gradient-to-r from-[var(--color-brand-from)] to-[var(--color-brand-to)] bg-clip-text text-transparent" data-astro-cid-mw7aashj> <span class="text-5xl" data-astro-cid-mw7aashj>↫</span> <span data-astro-cid-mw7aashj>loopwind</span> </h1> </a> <nav data-astro-cid-mw7aashj> <ul class="space-y-1" data-astro-cid-mw7aashj> <li data-astro-cid-mw7aashj> <a href="/" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Getting Started </a> </li><li data-astro-cid-mw7aashj> <a href="/templates" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Templates </a> </li><li data-astro-cid-mw7aashj> <a href="/images" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Images </a> </li><li data-astro-cid-mw7aashj> <a href="/video" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Video </a> </li><li data-astro-cid-mw7aashj> <a href="/animation" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Animation </a> </li><li data-astro-cid-mw7aashj> <a href="/helpers" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Helpers </a> </li><li data-astro-cid-mw7aashj> <a href="/styling" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Styling </a> </li><li data-astro-cid-mw7aashj> <a href="/fonts" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Fonts </a> </li><li data-astro-cid-mw7aashj> <a href="/agents" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> Agents </a> </li><li data-astro-cid-mw7aashj> <a href="/sdk" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> SDK </a> </li><li data-astro-cid-mw7aashj> <a href="/llm.txt" class="block px-3 py-2 rounded-md text-sm transition-colors no-underline text-muted-foreground hover:text-foreground hover:bg-accent/50" data-astro-cid-mw7aashj> llm.txt </a> </li> </ul> </nav> </div> </aside> <!-- Main content --> <main class="ml-64" data-astro-cid-mw7aashj> <div class="p-12" data-astro-cid-mw7aashj> <article class="prose prose-invert max-w-4xl" data-astro-cid-mw7aashj> <h1 id="sdk">SDK</h1>
2
+ <p>Use <strong>loopwind</strong> programmatically in your Next.js API routes, serverless functions, or Node.js applications. Perfect for building image/video generation APIs!</p>
3
+ <h2 id="installation">Installation</h2>
4
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> loopwind</span></span>
5
+ <span class="line"></span></code></pre>
6
+ <h2 id="quick-start">Quick Start</h2>
7
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
8
+ <span class="line"></span>
9
+ <span class="line"><span style="color:#6A737D">// Define template programmatically</span></span>
10
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> ogTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
11
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
12
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
13
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
14
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
15
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
16
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl text-white/80&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{description}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
17
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
18
+ <span class="line"><span style="color:#E1E4E8"> ),</span></span>
19
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
20
+ <span class="line"></span>
21
+ <span class="line"><span style="color:#6A737D">// Render to buffer</span></span>
22
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
23
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Hello World&#39;</span><span style="color:#E1E4E8">,</span></span>
24
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;Generated with loopwind SDK&#39;</span><span style="color:#E1E4E8">,</span></span>
25
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
26
+ <span class="line"></span></code></pre>
27
+ <h2 id="core-functions">Core Functions</h2>
28
+ <h3 id="definetemplate">defineTemplate()</h3>
29
+ <p>Define a template programmatically without any file system dependencies:</p>
30
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
31
+ <span class="line"></span>
32
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
33
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;my-template&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// Template name</span></span>
34
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;image&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// &#39;image&#39; or &#39;video&#39;</span></span>
35
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
36
+ <span class="line"></span>
37
+ <span class="line"><span style="color:#6A737D"> // For videos only</span></span>
38
+ <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
39
+ <span class="line"></span>
40
+ <span class="line"><span style="color:#6A737D"> // Optional fonts</span></span>
41
+ <span class="line"><span style="color:#E1E4E8"> fonts: [</span><span style="color:#9ECBFF">&#39;Inter&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;Roboto&#39;</span><span style="color:#E1E4E8">],</span></span>
42
+ <span class="line"></span>
43
+ <span class="line"><span style="color:#6A737D"> // The render function</span></span>
44
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
45
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-white&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
46
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
47
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
48
+ <span class="line"><span style="color:#E1E4E8"> ),</span></span>
49
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
50
+ <span class="line"></span></code></pre>
51
+ <h3 id="renderimage">renderImage()</h3>
52
+ <p>Render an image template to a Buffer:</p>
53
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
54
+ <span class="line"></span>
55
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> buffer</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, props, {</span></span>
56
+ <span class="line"><span style="color:#E1E4E8"> format: </span><span style="color:#9ECBFF">&#39;png&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// &#39;png&#39;, &#39;jpeg&#39;, &#39;jpg&#39;, &#39;webp&#39;, &#39;svg&#39;</span></span>
57
+ <span class="line"><span style="color:#E1E4E8"> quality: </span><span style="color:#79B8FF">92</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// JPEG/WebP quality (1-100)</span></span>
58
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
59
+ <span class="line"></span></code></pre>
60
+ <p><strong>Supported formats:</strong></p>
61
+ <ul>
62
+ <li><code>png</code> - Best for graphics with transparency</li>
63
+ <li><code>jpeg</code>/<code>jpg</code> - Smaller file size for photos</li>
64
+ <li><code>webp</code> - Modern format, good compression</li>
65
+ <li><code>svg</code> - Vector format, infinitely scalable</li>
66
+ </ul>
67
+ <h3 id="rendervideo">renderVideo()</h3>
68
+ <p>Render a video template to an MP4 Buffer:</p>
69
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
70
+ <span class="line"></span>
71
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> buffer</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(template, props, {</span></span>
72
+ <span class="line"><span style="color:#E1E4E8"> quality: </span><span style="color:#79B8FF">23</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// H.264 quality (0-51, lower = better)</span></span>
73
+ <span class="line"><span style="color:#B392F0"> onProgress</span><span style="color:#E1E4E8">: (</span><span style="color:#FFAB70">frame</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">total</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">phase</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
74
+ <span class="line"><span style="color:#E1E4E8"> console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">`${</span><span style="color:#E1E4E8">phase</span><span style="color:#9ECBFF">}: ${</span><span style="color:#E1E4E8">frame</span><span style="color:#9ECBFF">}/${</span><span style="color:#E1E4E8">total</span><span style="color:#9ECBFF">}`</span><span style="color:#E1E4E8">);</span></span>
75
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
76
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
77
+ <span class="line"></span></code></pre>
78
+ <h2 id="nextjs-api-routes">Next.js API Routes</h2>
79
+ <h3 id="image-generation">Image Generation</h3>
80
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// pages/api/og-image.ts</span></span>
81
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { NextApiRequest, NextApiResponse } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;next&#39;</span><span style="color:#E1E4E8">;</span></span>
82
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
83
+ <span class="line"></span>
84
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> ogTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
85
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
86
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
87
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
88
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12 justify-between&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
89
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center gap-4&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
90
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;w-12 h-12 rounded-full bg-white/20&#39;</span><span style="color:#E1E4E8">)} </span><span style="color:#F97583">/&gt;</span></span>
91
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">span style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-white/80 text-2xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">yoursite.com</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">span</span><span style="color:#F97583">&gt;</span></span>
92
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
93
+ <span class="line"><span style="color:#E1E4E8"> &lt;</span><span style="color:#B392F0">div</span><span style="color:#E1E4E8">&gt;</span></span>
94
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white mb-4&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
95
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl text-white/80&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{description}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
96
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
97
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
98
+ <span class="line"><span style="color:#E1E4E8"> ),</span></span>
99
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
100
+ <span class="line"></span>
101
+ <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiRequest</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiResponse</span><span style="color:#E1E4E8">) {</span></span>
102
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Welcome&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">description</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Generated with loopwind&#39;</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
103
+ <span class="line"></span>
104
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
105
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
106
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(description),</span></span>
107
+ <span class="line"><span style="color:#E1E4E8"> });</span></span>
108
+ <span class="line"></span>
109
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8">);</span></span>
110
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Cache-Control&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;s-maxage=86400, stale-while-revalidate&#39;</span><span style="color:#E1E4E8">);</span></span>
111
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(png);</span></span>
112
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
113
+ <span class="line"></span></code></pre>
114
+ <p><strong>Usage:</strong></p>
115
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="plaintext"><code><span class="line"><span>https://yoursite.com/api/og-image?title=Hello&amp;description=World</span></span>
116
+ <span class="line"><span></span></span></code></pre>
117
+ <h3 id="video-generation">Video Generation</h3>
118
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// pages/api/intro-video.ts</span></span>
119
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#F97583"> type</span><span style="color:#E1E4E8"> { NextApiRequest, NextApiResponse } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;next&#39;</span><span style="color:#E1E4E8">;</span></span>
120
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
121
+ <span class="line"></span>
122
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> introTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">({</span></span>
123
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;intro-video&#39;</span><span style="color:#E1E4E8">,</span></span>
124
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
125
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
126
+ <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
127
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
128
+ <span class="line"><span style="color:#6A737D"> // Fade in animation</span></span>
129
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> opacity</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> progress </span><span style="color:#F97583">&lt;</span><span style="color:#79B8FF"> 0.5</span><span style="color:#F97583"> ?</span><span style="color:#E1E4E8"> progress </span><span style="color:#F97583">/</span><span style="color:#79B8FF"> 0.5</span><span style="color:#F97583"> :</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">;</span></span>
130
+ <span class="line"></span>
131
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
132
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full bg-gradient-to-br from-black to-gray-900&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
133
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{ </span><span style="color:#F97583">...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold text-white&#39;</span><span style="color:#E1E4E8">), opacity }}</span><span style="color:#F97583">&gt;</span></span>
134
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
135
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
136
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
137
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
138
+ <span class="line"><span style="color:#E1E4E8"> },</span></span>
139
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
140
+ <span class="line"></span>
141
+ <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiRequest</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#F97583">:</span><span style="color:#B392F0"> NextApiResponse</span><span style="color:#E1E4E8">) {</span></span>
142
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#F97583"> =</span><span style="color:#9ECBFF"> &#39;Welcome!&#39;</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
143
+ <span class="line"></span>
144
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> mp4</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(introTemplate, {</span></span>
145
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#B392F0">String</span><span style="color:#E1E4E8">(title),</span></span>
146
+ <span class="line"><span style="color:#E1E4E8"> });</span></span>
147
+ <span class="line"></span>
148
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;video/mp4&#39;</span><span style="color:#E1E4E8">);</span></span>
149
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Cache-Control&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;s-maxage=3600&#39;</span><span style="color:#E1E4E8">);</span></span>
150
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(mp4);</span></span>
151
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
152
+ <span class="line"></span></code></pre>
153
+ <h2 id="template-props">Template Props</h2>
154
+ <p>Templates receive these props in their render function:</p>
155
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({</span></span>
156
+ <span class="line"><span style="color:#E1E4E8"> tw, </span><span style="color:#6A737D">// Tailwind style function</span></span>
157
+ <span class="line"><span style="color:#E1E4E8"> qr, </span><span style="color:#6A737D">// QR code generator</span></span>
158
+ <span class="line"><span style="color:#E1E4E8"> image, </span><span style="color:#6A737D">// Image embedding helper</span></span>
159
+ <span class="line"><span style="color:#E1E4E8"> template, </span><span style="color:#6A737D">// Template composition helper</span></span>
160
+ <span class="line"><span style="color:#E1E4E8"> frame, </span><span style="color:#6A737D">// Current frame (video only)</span></span>
161
+ <span class="line"><span style="color:#E1E4E8"> progress, </span><span style="color:#6A737D">// Progress 0-1 (video only)</span></span>
162
+ <span class="line"><span style="color:#F97583"> ...</span><span style="color:#E1E4E8">props </span><span style="color:#6A737D">// Your custom props</span></span>
163
+ <span class="line"><span style="color:#E1E4E8">}) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* JSX */</span><span style="color:#E1E4E8">)</span></span>
164
+ <span class="line"></span></code></pre>
165
+ <h3 id="using-tw">Using tw()</h3>
166
+ <p>The <code>tw</code> function converts Tailwind classes to inline styles:</p>
167
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
168
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col gap-4 p-8 bg-gradient-to-br from-blue-600 to-purple-700&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
169
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold text-white&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
170
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
171
+ <span class="line"><span style="color:#E1E4E8">)</span></span>
172
+ <span class="line"></span></code></pre>
173
+ <h3 id="video-animations">Video Animations</h3>
174
+ <p>Use Tailwind-style animation classes for easy video animations!</p>
175
+ <h4 id="transition-animations">Transition Animations</h4>
176
+ <p>Format: <code>animate-{type}/{start}/{end}</code> with optional easing</p>
177
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
178
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
179
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Fade in from progress 0 to 0.5 */</span><span style="color:#E1E4E8">}</span></span>
180
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold animate-fade-in/0/0.5&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
181
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
182
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
183
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
184
+ <span class="line"><span style="color:#E1E4E8">)</span></span>
185
+ <span class="line"></span></code></pre>
186
+ <p><strong>Available transition animations:</strong></p>
187
+ <p><em>Fade:</em></p>
188
+ <ul>
189
+ <li><code>animate-fade-in/0/1</code> - Fade in</li>
190
+ <li><code>animate-fade-out/0/1</code> - Fade out</li>
191
+ <li><code>animate-fade-in-up/0/1</code> - Fade in + slide up</li>
192
+ <li><code>animate-fade-in-down/0/1</code> - Fade in + slide down</li>
193
+ <li><code>animate-fade-in-left/0/1</code> - Fade in + slide from left</li>
194
+ <li><code>animate-fade-in-right/0/1</code> - Fade in + slide from right</li>
195
+ <li><code>animate-fade-out-up/0/1</code> - Fade out + slide up</li>
196
+ <li><code>animate-fade-out-down/0/1</code> - Fade out + slide down</li>
197
+ </ul>
198
+ <p><em>Slide:</em></p>
199
+ <ul>
200
+ <li><code>animate-slide-left/0/1</code> - Slide in from left (100px)</li>
201
+ <li><code>animate-slide-right/0/1</code> - Slide in from right (100px)</li>
202
+ <li><code>animate-slide-up/0/1</code> - Slide in from bottom (100px)</li>
203
+ <li><code>animate-slide-down/0/1</code> - Slide in from top (100px)</li>
204
+ </ul>
205
+ <p><em>Bounce:</em></p>
206
+ <ul>
207
+ <li><code>animate-bounce-in/0/1</code> - Bounce in with scale overshoot</li>
208
+ <li><code>animate-bounce-in-up/0/1</code> - Bounce in from below</li>
209
+ <li><code>animate-bounce-in-down/0/1</code> - Bounce in from above</li>
210
+ <li><code>animate-bounce-in-left/0/1</code> - Bounce in from left</li>
211
+ <li><code>animate-bounce-in-right/0/1</code> - Bounce in from right</li>
212
+ </ul>
213
+ <p><em>Scale &amp; Zoom:</em></p>
214
+ <ul>
215
+ <li><code>animate-scale-in/0/1</code> - Scale up from 50%</li>
216
+ <li><code>animate-scale-out/0/1</code> - Scale up and fade out</li>
217
+ <li><code>animate-zoom-in/0/1</code> - Zoom in from 0%</li>
218
+ <li><code>animate-zoom-out/0/1</code> - Zoom out to 200%</li>
219
+ </ul>
220
+ <p><em>Rotate &amp; Flip:</em></p>
221
+ <ul>
222
+ <li><code>animate-rotate-in/0/1</code> - Rotate in (180°)</li>
223
+ <li><code>animate-rotate-out/0/1</code> - Rotate out (180°)</li>
224
+ <li><code>animate-flip-in-x/0/1</code> - Flip in horizontally</li>
225
+ <li><code>animate-flip-in-y/0/1</code> - Flip in vertically</li>
226
+ </ul>
227
+ <p><strong>Easing modifiers:</strong></p>
228
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// Add easing before animation class</span></span>
229
+ <span class="line"><span style="color:#F97583">&lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;ease-in-out animate-fade-in/0/0.5&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
230
+ <span class="line"></span></code></pre>
231
+ <ul>
232
+ <li><code>linear</code> - Linear</li>
233
+ <li><code>ease-in</code> - Accelerate</li>
234
+ <li><code>ease-out</code> - Decelerate (default)</li>
235
+ <li><code>ease-in-out</code> - Accelerate then decelerate</li>
236
+ <li><code>ease-in-cubic</code> - Strong accelerate</li>
237
+ <li><code>ease-out-cubic</code> - Strong decelerate</li>
238
+ <li><code>ease-in-out-cubic</code> - Strong ease in/out</li>
239
+ </ul>
240
+ <h4 id="loop-animations">Loop Animations</h4>
241
+ <p>Format: <code>animate-{type}/{frameLength}</code></p>
242
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
243
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
244
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Pulse every 15 frames */</span><span style="color:#E1E4E8">}</span></span>
245
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold animate-pulse/15&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
246
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
247
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
248
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
249
+ <span class="line"><span style="color:#E1E4E8">)</span></span>
250
+ <span class="line"></span></code></pre>
251
+ <p><strong>Available loop animations:</strong></p>
252
+ <ul>
253
+ <li><code>animate-pulse/10</code> - Opacity pulse</li>
254
+ <li><code>animate-bounce/10</code> - Bounce up and down</li>
255
+ <li><code>animate-spin/30</code> - Full rotation</li>
256
+ <li><code>animate-ping/20</code> - Scale up and fade out</li>
257
+ <li><code>animate-wiggle/10</code> - Side to side wiggle</li>
258
+ <li><code>animate-float/30</code> - Gentle floating</li>
259
+ </ul>
260
+ <h4 id="staggered-animations">Staggered Animations</h4>
261
+ <p>Combine multiple animations with different timings:</p>
262
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
263
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
264
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Title fades in first */</span><span style="color:#E1E4E8">}</span></span>
265
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold ease-out animate-slide-up/0/0.3&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
266
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
267
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
268
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#6A737D">/* Subtitle fades in after */</span><span style="color:#E1E4E8">}</span></span>
269
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl mt-4 ease-out animate-slide-up/0.2/0.5&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
270
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">subtitle</span><span style="color:#E1E4E8">}</span></span>
271
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
272
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
273
+ <span class="line"><span style="color:#E1E4E8">)</span></span>
274
+ <span class="line"></span></code></pre>
275
+ <h4 id="manual-animations">Manual Animations</h4>
276
+ <p>You can still use <code>progress</code> and <code>frame</code> directly for custom animations:</p>
277
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#B392F0">render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">frame</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
278
+ <span class="line"><span style="color:#6A737D"> // Custom easing</span></span>
279
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> opacity</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> Math.</span><span style="color:#B392F0">pow</span><span style="color:#E1E4E8">(progress, </span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">);</span></span>
280
+ <span class="line"></span>
281
+ <span class="line"><span style="color:#6A737D"> // Custom transform</span></span>
282
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> rotation</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> Math.</span><span style="color:#B392F0">sin</span><span style="color:#E1E4E8">(frame </span><span style="color:#F97583">*</span><span style="color:#79B8FF"> 0.1</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">*</span><span style="color:#79B8FF"> 10</span><span style="color:#E1E4E8">;</span></span>
283
+ <span class="line"></span>
284
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> (</span></span>
285
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex items-center justify-center w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
286
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{</span></span>
287
+ <span class="line"><span style="color:#F97583"> ...</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-8xl font-bold&#39;</span><span style="color:#E1E4E8">),</span></span>
288
+ <span class="line"><span style="color:#E1E4E8"> opacity,</span></span>
289
+ <span class="line"><span style="color:#B392F0"> transform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">`rotate(${</span><span style="color:#E1E4E8">rotation</span><span style="color:#9ECBFF">}deg)`</span></span>
290
+ <span class="line"><span style="color:#E1E4E8"> }}</span><span style="color:#F97583">&gt;</span></span>
291
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">}</span></span>
292
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
293
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
294
+ <span class="line"><span style="color:#E1E4E8"> );</span></span>
295
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
296
+ <span class="line"></span></code></pre>
297
+ <h2 id="template-registry">Template Registry</h2>
298
+ <p>Create a registry of reusable templates:</p>
299
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// lib/templates.ts</span></span>
300
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
301
+ <span class="line"></span>
302
+ <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> templates</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> {</span></span>
303
+ <span class="line"><span style="color:#E1E4E8"> ogImage: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
304
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
305
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
306
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
307
+ <span class="line"><span style="color:#E1E4E8"> }),</span></span>
308
+ <span class="line"></span>
309
+ <span class="line"><span style="color:#E1E4E8"> twitterCard: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
310
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;twitter-card&#39;</span><span style="color:#E1E4E8">,</span></span>
311
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">600</span><span style="color:#E1E4E8"> },</span></span>
312
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
313
+ <span class="line"><span style="color:#E1E4E8"> }),</span></span>
314
+ <span class="line"></span>
315
+ <span class="line"><span style="color:#E1E4E8"> videoIntro: </span><span style="color:#B392F0">defineTemplate</span><span style="color:#E1E4E8">({</span></span>
316
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;video-intro&#39;</span><span style="color:#E1E4E8">,</span></span>
317
+ <span class="line"><span style="color:#E1E4E8"> type: </span><span style="color:#9ECBFF">&#39;video&#39;</span><span style="color:#E1E4E8">,</span></span>
318
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1920</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">1080</span><span style="color:#E1E4E8"> },</span></span>
319
+ <span class="line"><span style="color:#E1E4E8"> video: { fps: </span><span style="color:#79B8FF">30</span><span style="color:#E1E4E8">, duration: </span><span style="color:#79B8FF">3</span><span style="color:#E1E4E8"> },</span></span>
320
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">progress</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span><span style="color:#6A737D">/* ... */</span><span style="color:#E1E4E8">),</span></span>
321
+ <span class="line"><span style="color:#E1E4E8"> }),</span></span>
322
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
323
+ <span class="line"></span>
324
+ <span class="line"><span style="color:#6A737D">// pages/api/render/[template].ts</span></span>
325
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { templates } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;@/lib/templates&#39;</span><span style="color:#E1E4E8">;</span></span>
326
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { renderImage, renderVideo } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
327
+ <span class="line"></span>
328
+ <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> default</span><span style="color:#F97583"> async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> handler</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">req</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#E1E4E8">) {</span></span>
329
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#FFAB70">template</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">templateName</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.query;</span></span>
330
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> template</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> templates[templateName];</span></span>
331
+ <span class="line"></span>
332
+ <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">template) {</span></span>
333
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">status</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">404</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">({ error: </span><span style="color:#9ECBFF">&#39;Template not found&#39;</span><span style="color:#E1E4E8"> });</span></span>
334
+ <span class="line"><span style="color:#E1E4E8"> }</span></span>
335
+ <span class="line"></span>
336
+ <span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (template.type </span><span style="color:#F97583">===</span><span style="color:#9ECBFF"> &#39;video&#39;</span><span style="color:#E1E4E8">) {</span></span>
337
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> mp4</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderVideo</span><span style="color:#E1E4E8">(template, req.query);</span></span>
338
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;video/mp4&#39;</span><span style="color:#E1E4E8">);</span></span>
339
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(mp4);</span></span>
340
+ <span class="line"><span style="color:#E1E4E8"> } </span><span style="color:#F97583">else</span><span style="color:#E1E4E8"> {</span></span>
341
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, req.query);</span></span>
342
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">setHeader</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8">);</span></span>
343
+ <span class="line"><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">send</span><span style="color:#E1E4E8">(png);</span></span>
344
+ <span class="line"><span style="color:#E1E4E8"> }</span></span>
345
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
346
+ <span class="line"></span></code></pre>
347
+ <h2 id="typescript-support">TypeScript Support</h2>
348
+ <p>Define typed props for your templates:</p>
349
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#F97583">interface</span><span style="color:#B392F0"> OGImageProps</span><span style="color:#E1E4E8"> {</span></span>
350
+ <span class="line"><span style="color:#FFAB70"> title</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
351
+ <span class="line"><span style="color:#FFAB70"> description</span><span style="color:#F97583">:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
352
+ <span class="line"><span style="color:#FFAB70"> author</span><span style="color:#F97583">?:</span><span style="color:#79B8FF"> string</span><span style="color:#E1E4E8">;</span></span>
353
+ <span class="line"><span style="color:#E1E4E8">}</span></span>
354
+ <span class="line"></span>
355
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> ogTemplate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> defineTemplate</span><span style="color:#E1E4E8">&lt;</span><span style="color:#B392F0">OGImageProps</span><span style="color:#E1E4E8">&gt;({</span></span>
356
+ <span class="line"><span style="color:#E1E4E8"> name: </span><span style="color:#9ECBFF">&#39;og-image&#39;</span><span style="color:#E1E4E8">,</span></span>
357
+ <span class="line"><span style="color:#E1E4E8"> size: { width: </span><span style="color:#79B8FF">1200</span><span style="color:#E1E4E8">, height: </span><span style="color:#79B8FF">630</span><span style="color:#E1E4E8"> },</span></span>
358
+ <span class="line"><span style="color:#B392F0"> render</span><span style="color:#E1E4E8">: ({ </span><span style="color:#FFAB70">tw</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">title</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">description</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> }) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> (</span></span>
359
+ <span class="line"><span style="color:#6A737D"> // Props are fully typed!</span></span>
360
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">div style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;flex flex-col w-full h-full&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span></span>
361
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">h1 style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-6xl font-bold&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{title}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">h1</span><span style="color:#F97583">&gt;</span></span>
362
+ <span class="line"><span style="color:#F97583"> &lt;</span><span style="color:#E1E4E8">p style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-2xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">{description}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">p</span><span style="color:#F97583">&gt;</span></span>
363
+ <span class="line"><span style="color:#E1E4E8"> {</span><span style="color:#FFAB70">author</span><span style="color:#E1E4E8"> &amp;&amp; &lt;</span><span style="color:#FFAB70">span</span><span style="color:#FFAB70"> style</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{</span><span style="color:#B392F0">tw</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;text-xl&#39;</span><span style="color:#E1E4E8">)}</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">By {author}</span><span style="color:#F97583">&lt;/</span><span style="color:#E1E4E8">span</span><span style="color:#F97583">&gt;</span><span style="color:#E1E4E8">}</span></span>
364
+ <span class="line"><span style="color:#F97583"> &lt;/</span><span style="color:#E1E4E8">div</span><span style="color:#F97583">&gt;</span></span>
365
+ <span class="line"><span style="color:#E1E4E8"> ),</span></span>
366
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
367
+ <span class="line"></span>
368
+ <span class="line"><span style="color:#6A737D">// TypeScript will enforce correct props</span></span>
369
+ <span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(ogTemplate, {</span></span>
370
+ <span class="line"><span style="color:#E1E4E8"> title: </span><span style="color:#9ECBFF">&#39;Hello&#39;</span><span style="color:#E1E4E8">,</span></span>
371
+ <span class="line"><span style="color:#E1E4E8"> description: </span><span style="color:#9ECBFF">&#39;World&#39;</span><span style="color:#E1E4E8">,</span></span>
372
+ <span class="line"><span style="color:#6A737D"> // author is optional</span></span>
373
+ <span class="line"><span style="color:#E1E4E8">});</span></span>
374
+ <span class="line"></span></code></pre>
375
+ <h2 id="deployment">Deployment</h2>
376
+ <h3 id="vercel">Vercel</h3>
377
+ <p>The SDK works out of the box on Vercel serverless functions:</p>
378
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> loopwind</span></span>
379
+ <span class="line"><span style="color:#B392F0">vercel</span><span style="color:#9ECBFF"> deploy</span></span>
380
+ <span class="line"></span></code></pre>
381
+ <h3 id="netlify">Netlify</h3>
382
+ <p>Works with Netlify Functions:</p>
383
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="typescript"><code><span class="line"><span style="color:#6A737D">// netlify/functions/og-image.ts</span></span>
384
+ <span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { defineTemplate, renderImage } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> &#39;loopwind/sdk&#39;</span><span style="color:#E1E4E8">;</span></span>
385
+ <span class="line"></span>
386
+ <span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#B392F0"> handler</span><span style="color:#F97583"> =</span><span style="color:#F97583"> async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">event</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=&gt;</span><span style="color:#E1E4E8"> {</span></span>
387
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">title</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> event.queryStringParameters </span><span style="color:#F97583">||</span><span style="color:#E1E4E8"> {};</span></span>
388
+ <span class="line"></span>
389
+ <span class="line"><span style="color:#F97583"> const</span><span style="color:#79B8FF"> png</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#B392F0"> renderImage</span><span style="color:#E1E4E8">(template, { title });</span></span>
390
+ <span class="line"></span>
391
+ <span class="line"><span style="color:#F97583"> return</span><span style="color:#E1E4E8"> {</span></span>
392
+ <span class="line"><span style="color:#E1E4E8"> statusCode: </span><span style="color:#79B8FF">200</span><span style="color:#E1E4E8">,</span></span>
393
+ <span class="line"><span style="color:#E1E4E8"> headers: { </span><span style="color:#9ECBFF">&#39;Content-Type&#39;</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">&#39;image/png&#39;</span><span style="color:#E1E4E8"> },</span></span>
394
+ <span class="line"><span style="color:#E1E4E8"> body: png.</span><span style="color:#B392F0">toString</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">&#39;base64&#39;</span><span style="color:#E1E4E8">),</span></span>
395
+ <span class="line"><span style="color:#E1E4E8"> isBase64Encoded: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">,</span></span>
396
+ <span class="line"><span style="color:#E1E4E8"> };</span></span>
397
+ <span class="line"><span style="color:#E1E4E8">};</span></span>
398
+ <span class="line"></span></code></pre>
399
+ <h3 id="docker">Docker</h3>
400
+ <p>For self-hosted deployments:</p>
401
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="dockerfile"><code><span class="line"><span style="color:#F97583">FROM</span><span style="color:#E1E4E8"> node:20-alpine</span></span>
402
+ <span class="line"><span style="color:#F97583">WORKDIR</span><span style="color:#E1E4E8"> /app</span></span>
403
+ <span class="line"><span style="color:#F97583">COPY</span><span style="color:#E1E4E8"> package*.json ./</span></span>
404
+ <span class="line"><span style="color:#F97583">RUN</span><span style="color:#E1E4E8"> npm install</span></span>
405
+ <span class="line"><span style="color:#F97583">COPY</span><span style="color:#E1E4E8"> . .</span></span>
406
+ <span class="line"><span style="color:#F97583">RUN</span><span style="color:#E1E4E8"> npm run build</span></span>
407
+ <span class="line"><span style="color:#F97583">CMD</span><span style="color:#E1E4E8"> [</span><span style="color:#9ECBFF">&quot;npm&quot;</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&quot;start&quot;</span><span style="color:#E1E4E8">]</span></span>
408
+ <span class="line"></span></code></pre>
409
+ <h2 id="performance">Performance</h2>
410
+ <ul>
411
+ <li><strong>Images</strong>: ~50-100ms per image (PNG/JPEG/WebP)</li>
412
+ <li><strong>Videos</strong>: ~7s for 3-second Full HD video (1920×1080 @ 30fps)</li>
413
+ <li><strong>Pure JavaScript/WASM</strong>: No native dependencies, works everywhere</li>
414
+ </ul>
415
+ <h2 id="use-cases">Use Cases</h2>
416
+ <ul>
417
+ <li><strong>Dynamic OG images</strong> for blogs and marketing sites</li>
418
+ <li><strong>Programmatic videos</strong> for social media automation</li>
419
+ <li><strong>Email images</strong> generated on-the-fly</li>
420
+ <li><strong>User-generated content</strong> like certificates, cards, badges</li>
421
+ <li><strong>Batch processing</strong> of images or videos</li>
422
+ <li><strong>Image/video generation APIs</strong> that you can monetize</li>
423
+ </ul>
424
+ <h2 id="example-project">Example Project</h2>
425
+ <p>See the full Next.js example at <a href="https://github.com/tomtev/loopwind/tree/main/examples/nextjs-api">examples/nextjs-api/</a></p>
426
+ <pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8;overflow-x:auto" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">git</span><span style="color:#9ECBFF"> clone</span><span style="color:#9ECBFF"> https://github.com/tomtev/loopwind.git</span></span>
427
+ <span class="line"><span style="color:#79B8FF">cd</span><span style="color:#9ECBFF"> loopwind/examples/nextjs-api</span></span>
428
+ <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span></span>
429
+ <span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> run</span><span style="color:#9ECBFF"> dev</span></span>
430
+ <span class="line"></span></code></pre>
431
+ <p>Then visit:</p>
432
+ <ul>
433
+ <li><code>http://localhost:3000/api/og-image?title=Hello</code></li>
434
+ <li><code>http://localhost:3000/api/intro-video?title=Welcome</code></li>
435
+ </ul> </article> </div> </main> </div> </body></html>