I built Obsidian Remotion because I wanted a Markdown note to behave like a Remotion sketchbook.
The practical goal was pretty simple: write prose, drop in ts or tsx blocks, and get live, type-checked Remotion previews in a side pane without cramming React and Remotion into the plugin bundle itself.
That separation ended up being the whole project.
I wanted the plugin to stay relatively small and handle editor integration, diagnostics, preview lifecycle, and UI. I wanted the vault to own node_modules, so the actual React and Remotion runtime stayed under the user’s control. In practice that felt much less cursed than trying to smuggle a whole frontend runtime into an Obsidian plugin and then hoping version skew would somehow be fine.
The repo moved fast. The first commit landed on 2026-01-29, and by 2026-03-06 I already had a standalone remotion-md package, an Obsidian plugin package, runtime packages, a test vault, typechecking, bundling, scroll sync, and a whole lot of churn in the preview and iframe code. Looking back at the history, the arc is pretty clear: first I had to make Markdown extraction and synthesis real, then I had to make previews actually render, and then I had to spend a lot of time on resolution, runtime boundaries, and scroll behavior so the whole thing did not feel fake.
The workflow I was aiming for is:
In practice that means a note becomes something closer to a literate animation document than a plain markdown file.
The contract I wrote down in obsidian-remotion/README.md made the intent pretty explicit:
.md file defines a sequenceThat is a pretty opinionated model, and that is why I think it is interesting. I was not trying to make a generic “preview some code fences” plugin. I was trying to define a coherent authoring environment for timeline-shaped documents.
I could have stopped at “find code fence, run code, show output.” That would have been much easier. It also would have missed the point.
What I actually wanted was a markdown-native environment with enough semantics to support:
That is the useful part to me: it turns a markdown vault into a place where motion studies can live next to explanation, notes, and composition structure.
After many iterations I settled on splitting the concerns into a few bounded domains:
remotion-md: extraction, synthesis, compilation, bundling, and shared pipeline logicobsidian-remotion: the actual Obsidian plugin, view wiring, editor integration, status UI, and preview orchestrationexamples: a test vault with sample markdown notes and package setupThe top-level package.json also pins Remotion package versions across the workspace. That made sense for this project. Even though I wanted the vault to own the runtime, I still needed one stable view of what counted as a compatible toolchain while I was building the plugin and compiler pieces.
obsidian-remotion/src/main.ts is mostly coordination glue, which is exactly what I wanted the plugin layer to be:
remotion-md/src/pipeline.ts keeps the core flow pretty blunt: extract, classify, synthesize, bundle. I liked ending up there. It meant the compiler-ish parts were not permanently welded to Obsidian APIs.
The git history is compact but noisy in a very honest way. Reading through it now, I can see myself making the scary parts executable as quickly as possible and then discovering, one by one, which parts were actually going to be annoying.
The first run of commits on 2026-01-29 is very direct:
Initial plugin stubRunning plugin1. Extracting blocks2: Extraction & 3: SynthesisImplementing 4. Virtual filesystem hostThat sequence is basically the initial thesis statement. Before I worried too much about polish, I needed to prove that Markdown could be treated as a source format at all. So I went straight at extraction, synthesis, and a virtual compiler host.
Once the basic pipeline existed, I moved immediately into previews and bundling:
WIP bundlerWorking remotion example!Using preview function to render previewsBasic scene abstractionRough hanky demo of remotion in obsidianThis is where the idea stopped being theoretical for me. It is also where I started running into the problems that were always lurking under the demo: preview isolation, iframe messaging, scene identity, and the difference between a thing that renders once and a thing that keeps up with editing.
A surprising amount of churn landed in scroll and iframe code. The commit subjects tell the story pretty well:
Improve scroll syncMapping preview() locations across the paneProposal for scroll-sync algorithmSmooth scrollingFix bidirectional scroll echo preventionMoving mapping to iframeFix vertical offset in previewsThis did not feel decorative. If the promise is “write in the editor, understand the scene in the preview,” then navigation quality is part of the product. Bad sync would make the whole thing feel wonky immediately. I ended up pushing from rough positional behavior toward something more semantic and less fragile.
A few recurring problems show up pretty clearly both in the code and in the history.
This was one of the main technical fights.
I kept coming back to compiler and resolver code in commits like:
WIP: Fixing typescript module resolutionWIP: Module resolution. Need cleanupUnified module resolution. Why did this touch every file?Unify module resolutionSimplify resolution contextThat tracks with the design. Once Markdown files can import other Markdown files, and once bundling needs to work against the vault’s real node_modules, I am no longer doing a simple string transformation. I have to make TypeScript, esbuild, virtual files, and Obsidian editor state all agree on what a path means.
I wrote down pretty early that the iframe should be controlled, sandboxed, and disposable. The later history is basically me paying off that design choice:
That was the right place to spend effort. A preview system like this only feels trustworthy when runtime failures stay contained and reload behavior gets boring.
I did not want this to stop at just drawing players. I also wanted the note to feel enough like a real source document that editing it was not miserable.
That is where things like these commits came from:
Basic typescript diagnosticsWIP: language servertsserver integration improvementsIf a note is going to behave like a TSX source document, the editing experience has to meet that claim halfway.
By the end of this burst of work, this was not a toy plugin anymore.
It supported a workflow I actually wanted:
I also still like the architectural constraint I landed on. The plugin is not trying to become the package manager, runtime, renderer, and user project all at once. It mostly coordinates. The vault owns the heavy dependencies. remotion-md owns the compiler-ish steps. That division felt practical then, and it still does.
I bought flexibility here by accepting some complexity.
The trade looked like this:
I do not think that is a bad trade. It is just the actual shape of the project.
The main story, for me, is that I took a fuzzy but interesting idea — treating Markdown as a host language for timeline-oriented Remotion work inside Obsidian — and pushed it far enough that the hard parts had to become real infrastructure.
That includes:
That is the part I would point at in a portfolio. The project is opinionated about what the tool should feel like, and I kept following that idea into the messy parts instead of backing away once the demo worked.
This history covers a fast burst from late January to early March 2026. It already looks capable to me, but it also still looks alive in the way good tool projects usually do: lots of refactors, lots of tests showing up around runtime boundaries, and several commits that basically amount to “this got weird, I am fixing it anyway.”
Honestly, that makes me trust the result more.