BlogContact

Why I Keep Video Projects in JSON Before I Render Them

I used to let the renderer own the project file. That worked until I needed to change how the same scene was exported in different places. With VideoFlow, I prefer to keep the project in portable JSON first and let rendering happen later. That is the part that makes the stack easier to diff, easier to review, and easier to hand to an AI agent without dragging a timeline UI into every change.
If you want the official docs, I would start with the VideoFlow home page, then the core docs and renderers docs. The public repo is on GitHub, and the optional editor lives in the React video editor docs.

Why I moved the project source into JSON

I wrote about the prompt-to-VideoJSON step in How I Turn a Prompt Into Structured VideoJSON With VideoFlow, but the bigger change is psychological: once the project is data, I stop treating export as the place where decisions happen. Export becomes a repeatable step, not the place where the project lives.
That matters for a few practical reasons:
  • Git reviews can show actual scene changes instead of opaque timeline noise.
  • AI tools can emit structured output instead of trying to fake a manual edit.
  • I can re-render the same scene later without reconstructing the project.
  • I can keep one source for browser export, server rendering, and live preview.
  • If the render backend changes, the project does not need to.
VideoFlow is open source under Apache-2.0, which matters here. When the format is portable, I am less worried about getting trapped in one rendering path.

One project, three renderers

I already covered the export side in How to Build a Portable Video Export Pipeline in VideoFlow and the multi-backend pattern in How I Build a Three-Renderer Video Workflow With VideoFlow. The reason I like that architecture is simple: the same VideoJSON can drive a browser export, a server render, or a live DOM preview.
I use the browser renderer when the user should export from inside the app. I use the server renderer when the job belongs in a queue, a CI run, or a batch pipeline. I use the DOM renderer when I want a live preview that behaves like the scene I am about to render.
That is also why JSON feels cleaner than a renderer-specific timeline file. If the project is just a structured object, I can validate it, store it, and compare it before any pixels are rendered. That is the same pattern I used in How to Build a Reviewable JSON-to-Video Pipeline With VideoFlow.

Where the React editor fits

If someone on the team needs a visual UI, the optional React video editor is the layer I would add on top, not the thing I would use as the source of truth. It gives me a multi-track timeline, keyframes, transitions, themes, and MP4 export, but it still works best when the underlying scene format stays portable.
For an internal tool or a SaaS product, that is the nice middle ground. Non-technical users get a real editor. The codebase still keeps a portable scene definition that can be versioned and rendered elsewhere.

The workflow I would actually ship

When I build around VideoFlow, I try to keep the workflow boring in the right way:
  1. Author the scene in TypeScript through <code>@videoflow/core</code>.
  2. Compile it to VideoJSON.
  3. Validate the output before I render anything.
  4. Preview it in the DOM renderer.
  5. Export in the browser or on the server depending on the job.
  6. Store the JSON alongside the product code so future changes are diffable.
  7. Let an agent generate or patch the JSON instead of rebuilding the whole video by hand.
That last step is why this stack feels better suited to automation than a traditional timeline-first workflow. I already wrote about the prompt-to-structured step, but the short version is that an agent handles JSON better than it handles a visual timeline.
A minimal example looks like this:
<pre><code>import VideoFlow from "@videoflow/core";
const $ = new VideoFlow({
name: "Product Demo",
width: 1920,
height: 1080,
fps: 30
});
$.addText({ text: "New feature launch", fontSize: 7, fontWeight: 800 });
const videoJSON = await $.compile();
const mp4 = await $.renderVideo();</code></pre>
I do not need that exact API shape in every project. What I need is the rule behind it: keep the project data first, then render it in the environment that makes sense.

What I would do next

If I were starting today, I would begin with the core docs, check the examples, and use the playground to test the shape of a scene before I wired it into an app.
That keeps the system small enough to maintain and flexible enough to survive a renderer change later.