Lovable + reelsdk
Render faceless shorts from a Lovable app
Lovable Cloud runs your backend as Supabase edge functions: 256 MB of memory, 2 s of CPU per request and no subprocesses, so ffmpeg has nowhere to run. reelsdk renders on its own machines. One call from an edge function turns a topic into a narrated, captioned short for $0.25 up to 60 s, and a signed webhook puts the mp4 in your videos table about 100 s later. The first $1 is free.
- One prompt sets up the table, two edge functions and the page
- A short renders in about 100 s on reelsdk machines
- Your key stays a write-only Lovable secret, never in the browser
Error: failed to import ffmpeg-core.js
Spawning subprocesses is not allowed on Supabase Edge Runtime.
The same job as one call to reelsdk
const REELSDK = "https://api.reelsdk.com";
const res = await fetch(`${REELSDK}/v1/shorts?wait=0`, {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("REELSDK_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ topic, style: "cinematic", webhook_url }),
});
const { job } = await res.json();Why ffmpeg breaks here
Two places to put ffmpeg, and neither works
Lovable Cloud builds its backend on Supabase, so the functions Lovable writes for you are Supabase Edge Functions running on Deno. Supabase documents 256 MB of memory and 2 s of CPU per request, and the runtime refuses to start a native binary with Spawning subprocesses is not allowed on Supabase Edge Runtime. An encode heavy enough to matter ends in HTTP 546 with WORKER_RESOURCE_LIMIT.
The other place is the browser. ffmpeg.wasm downloads a core of about 31 MB and encodes on the visitor's own device. That's fine for a desktop tool that trims one short clip. On a phone it stalls the tab, it stops when the user closes the page, and it can't make videos in the background, on a schedule or for many users at once.
Lovable Cloud next to reelsdk
| Limit | Lovable Cloud | reelsdk |
|---|---|---|
| Memory | 256 MB per edge function | Renders on reelsdk machines, inputs up to 500 MB each |
| CPU time | 2 s per request | Up to 10 min of run time per job |
| Native ffmpeg | Subprocesses are not allowed | ffmpeg 8.1 on the server, raw arguments through POST /v1/ffmpeg |
| In the browser | ffmpeg.wasm, about 31 MB per visitor | Nothing to download, the page shows an mp4 |
| Long jobs | The function has to return while the render runs | ?wait=0 plus a signed webhook, 3 delivery attempts |
Lovable limits from Supabase Edge Functions limits, checked October 2026.
Set it up
One prompt, two edge functions, one table
You need a reelsdk key and its webhook secret. Lovable writes the code from the prompt below. The snippets after it show what the two functions should look like, so you can check its work before you publish.
Paste the prompt into Lovable
The prompt asks for a
videostable with row level security and realtime, acreate-shortfunction that starts a render, areelsdk-webhookfunction that receives it, and a page that lists your videos. Lovable writes and deploys edge functions from chat, there is no create button.Prompt for Lovable Add faceless short videos to this app, rendered by reelsdk (https://reelsdk.com). 1. Ask me for two secrets: REELSDK_KEY and REELSDK_WEBHOOK_SECRET. Never use them in frontend code. 2. Create a table "videos": id uuid, user_id uuid, topic text, job_id text, status text default 'queued', video_url text, created_at timestamptz. Turn on RLS so users only read their own rows, and enable realtime on the table. 3. Create an edge function "create-short" for signed-in users. It takes { topic }, POSTs to https://api.reelsdk.com/v1/shorts?wait=0 with the header "Authorization: Bearer REELSDK_KEY" and the JSON { "topic": topic, "style": "cinematic", "duration_s": 45, "webhook_url": "<this project's functions URL>/reelsdk-webhook" }. The response is JSON { job } with HTTP 202. Insert a videos row with job_id = job.id and status = job.status. 4. Create an edge function "reelsdk-webhook" with JWT verification turned off. Read the raw body with req.text() before parsing. Verify the Reel-Signature header (format t=<unix seconds>,v1=<hex>): it is the HMAC-SHA256 of "<t>.<raw body>" keyed with REELSDK_WEBHOOK_SECRET. Return 401 if it doesn't match or t is more than 300 seconds old. Then update the videos row where job_id = job.id with status = job.status and video_url = job.outputs.video. 5. On the dashboard, add a topic field and a Generate button that calls create-short, and list the user's videos with a player once the status is "succeeded".Add the key and the webhook secret
When Lovable asks for them, paste both values into the secure form in the chat, or add them under More, Settings, Secrets. Lovable encrypts secrets, injects them into edge functions and never sends them to the browser. They are write-only, and a remix doesn't copy them, so add them again in a remixed project.
Lovable secrets - REELSDK_KEY
- rk_live_...
- REELSDK_WEBHOOK_SECRET
- whsec_...
Check the create-short function
It should check the signed-in user, call
POST /v1/shortswith?wait=0and yourwebhook_url, and savejob.idfrom the{ job }envelope on a new row invideos. With?wait=0reelsdk answers with a 202 right away, so the function returns in well under a second while the short renders.supabase/functions/create-short/index.ts import { createClient } from "npm:@supabase/supabase-js@2"; const cors = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") return new Response(null, { headers: cors }); const supabase = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_ANON_KEY")!, { global: { headers: { Authorization: req.headers.get("Authorization") ?? "" } }, }); const { data } = await supabase.auth.getUser(); if (!data.user) return new Response("sign in first", { status: 401, headers: cors }); const { topic } = await req.json(); const res = await fetch("https://api.reelsdk.com/v1/shorts?wait=0", { method: "POST", headers: { Authorization: `Bearer ${Deno.env.get("REELSDK_KEY")}`, "Content-Type": "application/json", }, body: JSON.stringify({ topic, style: "cinematic", duration_s: 45, webhook_url: `${Deno.env.get("SUPABASE_URL")}/functions/v1/reelsdk-webhook`, }), }); const { job, error } = await res.json(); if (!res.ok) return Response.json({ error }, { status: res.status, headers: cors }); await supabase.from("videos").insert({ user_id: data.user.id, topic, job_id: job.id, status: job.status }); return Response.json({ job_id: job.id }, { headers: cors }); });Check the webhook function
JWT verification has to be off for this one function so reelsdk can reach it, which makes the signature check the lock on the door. It reads the raw body, verifies
Reel-Signatureagainst yourwhsec_secret, rejects timestamps more than 300 s old and writes the mp4 link to the matching row. A 500 makes reelsdk retry, up to 3 attempts.supabase/functions/reelsdk-webhook/index.ts import { createClient } from "npm:@supabase/supabase-js@2"; const admin = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!); Deno.serve(async (req) => { const body = await req.text(); const signature = req.headers.get("Reel-Signature"); if (!(await verifyReelSignature(signature, body, Deno.env.get("REELSDK_WEBHOOK_SECRET")!))) { return new Response("invalid signature", { status: 401 }); } const job = JSON.parse(body); const { error } = await admin .from("videos") .update({ status: job.status, video_url: job.outputs?.video ?? null }) .eq("job_id", job.id); // A 500 makes reelsdk retry the delivery if (error) return new Response(error.message, { status: 500 }); return new Response("ok"); }); async function verifyReelSignature(header: string | null, body: string, secret: string) { const parts = Object.fromEntries((header ?? "").split(",").map((part) => part.split("="))); const age = Math.abs(Date.now() / 1000 - Number(parts.t)); if (!/^[0-9a-f]{64}$/i.test(parts.v1 ?? "") || !(age <= 300)) return false; const encoder = new TextEncoder(); const key = await crypto.subtle.importKey( "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"], ); const signature = Uint8Array.from(parts.v1.match(/../g), (pair: string) => parseInt(pair, 16)); return crypto.subtle.verify("HMAC", key, signature, encoder.encode(`${parts.t}.${body}`)); }
FAQ
Video on Lovable, answered
Why can't I run ffmpeg inside a Lovable edge function?
Lovable Cloud runs edge functions on Supabase's Edge Runtime, which allows 256 MB of memory and 2 s of CPU per request. Starting a binary throws "Spawning subprocesses is not allowed on Supabase Edge Runtime." and heavy memory or CPU work ends in HTTP 546 WORKER_RESOURCE_LIMIT. Send the render to an API and let the function wait or return.
Should I use ffmpeg.wasm in my Lovable frontend instead?
Only for small jobs on desktop. ffmpeg.wasm downloads a core of about 31 MB and encodes on the visitor's own device, and the render stops when the tab closes. If videos have to render reliably on phones, in the background or for many users, do the render on a server.
How long does a short take, and will my edge function time out?
A render machine is ready in about 1.9 s at the median and a faceless short takes about 100 s. Pass ?wait=0 and a webhook_url, as the prompt does, so create-short returns right away and the webhook updates the row when the mp4 is ready. Nothing waits on a wall clock limit.
Can I send my own ffmpeg commands through reelsdk?
Yes. POST /v1/ffmpeg takes your ffmpeg 8.1 arguments, without a shell, for $0.01 per compute minute. The higher-level endpoints do the common jobs in one call: /v1/slideshow and /v1/captions at $0.02 per output minute, /v1/shorts at $0.25 up to 60 s and $0.35 up to 90 s.
How do I get a key, and how does billing work?
Sign up with Google and create a key (rk_live_...) in the dashboard; it comes with a webhook secret (whsec_...). The first $1 is free and there is no subscription or minimum. Each key runs 3 jobs at once and up to 500 jobs a day, and mp4 download links stay up for 7 days.
The same fix on other platforms
All integrationsReady to build?
Ship video from your app today
Your first $1 of renders is free, and one key covers every endpoint.
