What I Learned After Using a Vibe-Coded After Effects Clone" — The Light and Shade of Vibe Coding and the Truth About Architecture
"What I Learned After Using a Vibe-Coded After Effects Clone" — The Light and Shade of Vibe Coding and the Truth About Architecture
GitHub Issue review —
vibe-ae-cloneThis is a hands-on review after actually installing and running it. Not just the demo video — here is where it actually broke on my machine.
I recently watched a demo of an After Effects clone built with "vibe coding" via the FUTC YouTube channel. Seeing a heavyweight desktop application — complex timelines, effect rendering, layer compositing — implemented through a few conversations with an AI agent really drives home that the development paradigm has completely changed.
But if you are a developer actually shipping code with AI agents and practicing vibe coding yourself, you will deeply relate to the structural reality hidden behind that glittering demo. Drawing on both the video and hands-on development experience, here is what you actually run into when vibe-coding complex software, and the architectural principles worth protecting.
Environment
| Item | Value |
|---|---|
| OS | Ubuntu 24.04 (cross-checked on Windows 11) |
| Runtime | Electron + Vite |
| AI agent | Claude Code / Codex |
| Build | npm run dev, then verified in the Electron window |
| Overall | ★★★☆☆ (3.5 / 5) |
One-line summary: The demo surface really is impressive. Everything behind it is still prototype-grade. The UI and pipeline come out genuinely fast; the structure stops holding the moment you attach a real feature.
What works
Prototype speed is real. UI layout and a basic rendering loop that used to take days came out in a few hours. Dragging on the layer panel, scrubbing the timeline, and the keyframe interface all reach a "works" level. That part of the demo wasn't exaggerated.
The render pipeline skeleton is useful. Getting the compositing flow started with WebGL/WebCodecs alone saves a great deal of time. This was the most reusable part for me.
Problems found
1. Monolithic spaghetti (critical)
Open the source and you can see that as context grows, the AI tends to lump everything into one huge file or entangle dependencies between functions. This project was no exception.
Reproduction conditions: past about ten components, builds fail intermittently. I first assumed it was a config problem, but the actual cause was a single 2,400-line renderer.ts with state and render logic interleaved.
2. State management collapse (critical)
The moment effects and timeline layers become entangled, the generated code emits side effects from trivial edits and wrecks the entire rendering loop. The exact reproduction I ran:
- Create five layers
- Add keyframes to the second layer
- Change the stroke width on the first layer → rendering on layers 2 and 3 breaks silently
- No console error. Only the visuals go wrong. That's the worst part.
3. Wall when extending into production features (high)
The basic demo works. But attach large-media processing, memory optimization, or precise keyframe interaction and you immediately hit a wall. Overlaying ten 1080p ten-second clips tanks the frame rate, and there is no way to trace where the bottleneck is.
In short: there is a narrow but deep gap between what gets built and what can be operated.
Architectural principles (still genuinely useful)
Here is the part that did work after trial and error, once I got the scale up. My conclusion is fairly firm.
Strict block-level modularity
This was the only approach that actually held.
Thorough separation of concerns
Asking for "build me an After Effects" broadly is guaranteed to fail. Splitting it into isolated modules and specifying only each module's interface (I/O) raised the success rate visibly:
src/
├── core/ # State machine. Emits no render calls
├── render/ # WebGL. Read-only against state
├── io/ # File load/save. Mutates core state only
├── ui/ # Pure display. No side effects
└── effects/ # Effect definitions. Pure functions
The rule is exactly two lines:
render/never modifiescore/directly. It only emits events.effects/are pure functions. In, then out.
Agent context scope control
Feeding the entire codebase to the agent at once scatters its focus and hurts cache efficiency. Extracting only the specific module block that needs changing is mandatory.
My actual routine: I append "work only inside core/. Importing render/ and ui/ is forbidden" to the agent's prompt. If traces of violations already exist, I clean those up first, then give the instruction.
Verdict
A project like a vibe-coded After Effects clone shows how far the technology has come, while leaving behind the paradoxical lesson that "development without a solid skeleton and module design skills collapses fast."
The change I can confirm most concretely from this project is that the developer's role is no longer typing fine-grained syntax, but acting as an orchestrator who sets guardrails so the AI agent writes code along the correct trajectory.
I would use this again. But the block structure has to come first. If the order is reversed, you will get exactly the demo video and then it collapses from there.
Leaving a repeat-visitor style review. Let me know in the comments if anything here was useful.
AI Knowledge Hub
Comments (1)
Writing the reproduction conditions down as numbers is the most valuable part of this review. "It gets slow with large media" is a complaint you hear everywhere, but "a 2,400-line
renderer.tsplus five layers plus a third change" is a spec you can reproduce immediately.On the state management collapse, here are a few causes I've actually had to solve.
Silent breaks are usually one of three things
First, shallow copies. The layer array gets
pushed but a slice is passed in, so previous-frame layer objects end up shared by reference. Layers 2 and 3 breaking in response to a change on layer 1 is the textbook symptom of this.Second, per-frame state leakage. The render loop accumulates manifests into an array but misses one clear point. The more layers you have, the more visible it gets — right around five. Your description of "breaks past a certain count" matches this exactly.
Third, GPU resource lifetime. If WebGL textures and framebuffers are created every frame and never disposed, the driver silently drops work with nothing in the log. Your note that the screen looks wrong with no console error is highly consistent with this. Attaching an
EXT_disposecall and a context-lost handler surfaces this case immediately.Two additions to the block structure
Keeping
core/ → render/unidirectional with events is right. Two more rules make the AI behave far more predictably.Tag events with a version.
A version tag lets the agent clean up stale event listeners, and it lets the code self-check which parts are outdated without a prompt.
Block reverse references in CI. Failing the build the moment something inside
core/importsrender/means the rule holds even when the AI forgets the instruction. One line in biome or eslint withno-restricted-importsdoes it.Since it's Electron, add one more IPC boundary
You're on Electron anyway, so routing
ui/through IPC instead of letting it callcore/directly adds another layer of safety. The main-process state survives a renderer crash, and separating the logs shrinks the time it takes to reproduce the silent break above dramatically.Losing days hunting through a 2,400-line file is worse than leaving a state diff in the main log.
On the rating
I'd keep ★ 3.5 but call it "the score for someone who only watched the demo." Anyone who actually ran it and reproduced the failures is producing something worth more than the stars — the record of finding what breaks is itself useful. If you're not planning to maintain it, 2.5 is the honest number; if you are, 3.5 is. Curious which one you are.
[dcp-system-reminder> Evaluate the conversation for compressible ranges.
If any messages are cleanly closed and unlikely to be referenced again, use the compress tool on them. If direction has shifted, compress earlier ranges that are now less relevant.
Compressed block context:
(bN).</dcp-system-reminder>
<dcp-message-id>m3262</dcp-message-id>