For the last two years I've been building Spread Out: you paste your website, and it plans a week of posts, generates the short videos, carousels and podcasts, and publishes them to Instagram, TikTok, YouTube, LinkedIn, Facebook, X and Threads.
V2 launches on Product Hunt tomorrow, so this felt like a good moment to write down the parts that were harder than they looked. None of this is a tutorial. It's the list I wish I'd had at the start.
The first version could make a decent video from a URL. The problem showed up in week three: the same brand got the same hook, the same "Did you know…" opener and the same b-roll, over and over. A single video looked fine. A feed of them looked like a bot.
What fixed it wasn't a better model. It was memory:
If you're building anything that generates content on a schedule, budget more time for state than for prompts.
LLM-written narration has a recognisable rhythm: three-item lists, "it's not just X, it's Y", every sentence the same length. Viewers scroll past it in a second.
Two things helped more than any prompt rewrite:
Small detail that mattered: overlay cards (facts, numbers) are timed to the word-level captions, so a card shows up when the voice actually says it rather than on a fixed beat.
Video comes from LTX-2.5 (it generates the speech natively, so talking-head clips don't need a separate TTS track), and stills come from Qwen-Image, both running on our own GPU servers behind a Gradio API. Per-call APIs are simpler to start with, but at "a week of videos per user" running our own GPUs gives us control over both cost and which models we use.
What I didn't expect was how much of the work became scheduling:
min(configured concurrency, healthy GPUs) in parallel, and each server tracks which model it currently has loaded so jobs land where the weights already are.
Captions, cards and transitions are HTML/CSS compositions animated with GSAP, then rendered frame by frame to video on the GPU servers. That means designers can iterate in a browser, and the same components drive carousels and video overlays.
The catch: the renderer seeks the timeline to arbitrary frames, so animations have to be pure functions of time. Anything that reads the DOM state or sets opacity outside the timeline will look right in the browser and flicker in the render.
This one is fresh. This afternoon the API stopped answering: health checks timing out, the database pool exhausted, simple requests taking six seconds.
The logs were full of this:
Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js
or incorrect usage of Node.js internals.
at internalConnectMultiple (node:net:1106:3)
at Timeout.internalConnectMultipleTimeout (node:net:1637:3)
The server image was pinned to Node 20.0.0. In that release, "happy eyeballs" (autoSelectFamily, on by default since Node 20) can hit an internal assertion when a connection attempt times out. We had a global uncaughtException handler that logged and carried on, which kept the process alive, but the sockets involved were left in a bad state until nothing could connect.
The immediate fix was a restart. The real fix is one line at startup:
import net from 'node:net';
net.setDefaultAutoSelectFamily(false);
(Upgrading Node also fixes it. If you're on an early 20.x and seeing internalConnectMultiple in your logs, check this first.) The broader lesson: an uncaughtException handler that swallows everything turns a crash, which restarts cleanly, into a slow freeze, which doesn't.
Scheduling posts to seven platforms sounds like plumbing. In practice: tokens expire, accounts get disconnected, one platform rejects videos over a certain length, another wants a verified account for long uploads. We ended up treating "generated" and "published" as separate states, checking the account's auth before posting, and surfacing failures in the UI instead of retrying silently.
V2 adds blog-to-social (new RSS articles become posts automatically), AI replies to comments and Instagram keyword-to-DM, an MCP server so you can drive it from Claude or Cursor, and bring-your-own keys for fal, Replicate and Higgsfield.
If you want to try it, the free plan has 350 credits and doesn't need a card: spreadout.ai. We're on Product Hunt tomorrow, and I'd love to hear where the output still feels machine-made. That's the feedback I act on first.
Happy to answer questions about any of the above in the comments.