<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MartinDelophy</title>
    <description>The latest articles on DEV Community by MartinDelophy (@martindelophy).</description>
    <link>https://dev.to/martindelophy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4029567%2Fc8d453a4-d3ea-4275-b7ce-fbece2440c21.png</url>
      <title>DEV Community: MartinDelophy</title>
      <link>https://dev.to/martindelophy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martindelophy"/>
    <language>en</language>
    <item>
      <title>Turning a README into a Playable Game Catalog</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 03:13:17 +0000</pubDate>
      <link>https://dev.to/martindelophy/turning-a-readme-into-a-playable-game-catalog-2g4h</link>
      <guid>https://dev.to/martindelophy/turning-a-readme-into-a-playable-game-catalog-2g4h</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/MartinDelophy/awesome-gpt-6-astra" rel="noopener noreferrer"&gt;awesome-gpt-6-astra&lt;/a&gt;&lt;br&gt;
Live gallery: &lt;a href="https://astragames.aigccreative.com/" rel="noopener noreferrer"&gt;Astra Games&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building a game with AI leaves another problem to solve: helping people find it, try it, and understand how it was made.&lt;/p&gt;

&lt;p&gt;I maintain Awesome GPT-6 Astra, a community collection of games and interactive projects. As the collection grew, a Markdown list became the content source for a visual gallery with screenshots and playable links.&lt;/p&gt;

&lt;p&gt;This post explains a few design choices behind that workflow: structured entries, a README-driven catalog, real screenshots, and multilingual maintenance.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Give each entry a predictable structure
&lt;/h2&gt;

&lt;p&gt;A collection becomes difficult to browse when every submission uses a different format. One project links to source code, another has a screenshot, and a third hides its demo inside a long development log.&lt;/p&gt;

&lt;p&gt;We use a small, consistent entry format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Project name → playable demo
  - Creator → public profile
  - Platform → browser requirements, controls, access conditions
  - GPT-6 Astra → model contribution statement or development record
  - Resources → source code and setup instructions, when available
  - Preview → an actual gameplay screenshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A demo answers “Can I try it?” Source code answers “Can I study or run it?” A development record answers “How was it built?” These fields serve different purposes.&lt;/p&gt;

&lt;p&gt;Public source code is optional. A playable project can still be useful to explore, provided its creator is credited and the available information is clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Let the README supply the website's catalog
&lt;/h2&gt;

&lt;p&gt;Maintaining a GitHub list and a separate frontend array creates duplicate work. A contributor can update the README while the gallery continues showing an old description—or misses the project entirely.&lt;/p&gt;

&lt;p&gt;Our gallery reads the public upstream README and parses it into structured records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;README entries
      ↓
Parse titles, categories, authors, links, and screenshots
      ↓
Structured catalog records
      ↓
Gallery cards and playable links
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contributors can work with Markdown and pull requests without learning the frontend. The website handles presentation and periodically checks for catalog updates.&lt;/p&gt;

&lt;p&gt;The tradeoff is that Markdown is now a content interface. Formatting conventions matter, and the parser needs to handle edits predictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Parse project entries, not every link
&lt;/h2&gt;

&lt;p&gt;A README also contains navigation, language selectors, contribution instructions, licenses, and creator profiles. Treating every link as a game would produce a noisy catalog.&lt;/p&gt;

&lt;p&gt;The parser needs to distinguish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project titles from navigation.&lt;/li&gt;
&lt;li&gt;Demo destinations from source-code references.&lt;/li&gt;
&lt;li&gt;Creator metadata from descriptions.&lt;/li&gt;
&lt;li&gt;Games from interactive experiments.&lt;/li&gt;
&lt;li&gt;Valid entries from incomplete or duplicate content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The existing tests cover adding, deleting, and editing entries, duplicate handling, malformed documents, caching, and recovery from failed reads.&lt;/p&gt;

&lt;p&gt;For a project like this, the useful guarantee is straightforward: a normal README contribution should produce the expected catalog update.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Make screenshots part of the entry
&lt;/h2&gt;

&lt;p&gt;A short description rarely communicates how a game feels. An actual screenshot helps readers understand its visual style and interface before opening it.&lt;/p&gt;

&lt;p&gt;We embed screenshots in the README entries so the gallery can use them as covers. For captured images stored in the repository, we also record their source, capture date, and the scope of the check.&lt;/p&gt;

&lt;p&gt;That last detail matters. Entering the first level is not a full playthrough. A manual describing touch controls is not evidence that someone tested the game on a phone.&lt;/p&gt;

&lt;p&gt;Keeping those distinctions explicit makes the catalog more useful to both readers and future maintainers. Screenshots should represent the running project, and third-party images retain their creators' rights.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Multilingual maintenance includes metadata
&lt;/h2&gt;

&lt;p&gt;The collection has 12 README language versions. Translating a description is only one part of keeping them aligned.&lt;/p&gt;

&lt;p&gt;For a new submission, we also check that every version includes the entry, uses the same demo and creator links, references an existing screenshot, and has consistent counts and maintenance dates.&lt;/p&gt;

&lt;p&gt;Model attribution needs the same care. If one version says “awaiting creator confirmation,” another should not describe the contribution as independently verified.&lt;/p&gt;

&lt;p&gt;The website's current catalog source is the default README. The translated READMEs are additional documentation that we maintain alongside it; translating them does not imply that every language is a separate runtime data source.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Describe AI involvement precisely
&lt;/h2&gt;

&lt;p&gt;“Made with GPT-6 Astra” can describe very different development processes: implementing one mechanic, helping design an interface, iterating on game rules, or contributing across a larger project.&lt;/p&gt;

&lt;p&gt;We aim to preserve those distinctions. Attribution follows creator or submitter statements, and missing confirmation is marked explicitly. Inclusion is not an independent model benchmark or an endorsement from OpenAI.&lt;/p&gt;

&lt;p&gt;A useful submission explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the player actually does.&lt;/li&gt;
&lt;li&gt;Which parts Astra helped build.&lt;/li&gt;
&lt;li&gt;What required iteration or manual changes.&lt;/li&gt;
&lt;li&gt;What limitations remain in the current version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These details give other developers something to learn from beyond a finished screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share something people can play
&lt;/h2&gt;

&lt;p&gt;The goal of this project is to give creators a place to show working games and connect their demos with useful development context.&lt;/p&gt;

&lt;p&gt;Playable prototypes are welcome. If you have built a game with Astra, you can submit it through the repository with a demo, creator credit, a real screenshot, and a description of the model's contribution.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: This article was drafted and translated with AI assistance from the maintainer's project notes and repository context.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
    </item>
    <item>
      <title>Making a React Video Editor Agent-Friendly with Timeline Markers and MCP</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Tue, 08 Sep 2026 06:51:06 +0000</pubDate>
      <link>https://dev.to/martindelophy/making-a-react-video-editor-agent-friendly-with-timeline-markers-and-mcp-k7h</link>
      <guid>https://dev.to/martindelophy/making-a-react-video-editor-agent-friendly-with-timeline-markers-and-mcp-k7h</guid>
      <description>&lt;p&gt;A video editor needs to remember more than cuts. It also needs to remember why a cut matters.&lt;/p&gt;

&lt;p&gt;“Start the second chapter here.” “Review this section before publishing.” “Leave a little more room at the end.” These instructions become much easier to act on when their timestamps and context live inside the project.&lt;/p&gt;

&lt;p&gt;I recently added persistent timeline markers to Timeline Studio, the open-source browser video editor I maintain, and exposed them through its CLI, MCP adapter, and Agent Skill. The interesting engineering work was making those annotations precise, inspectable, and safe to edit in a batch.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;try the editor in your browser&lt;/a&gt; or &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;browse the source on GitHub&lt;/a&gt;. The implementation discussed here is included in Skill v1.0.7. Timeline Studio uses React and Vite and follows a local-first approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give annotations their own data model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The editor supports four annotation types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Timing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;marker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A beat cue, action, or cut reference&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chapter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The beginning of a named section&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An interval to review or work on&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;time&lt;/code&gt;, &lt;code&gt;endTime&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Feedback tied to a precise moment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A review range looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product-review"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"endTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product reveal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Check whether the caption appears with the product."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"violet"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Times are absolute project seconds. Titles and notes preserve Unicode, and annotations are serialized in &lt;code&gt;timelineMarkers&lt;/code&gt; inside the portable &lt;code&gt;.timeline&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;One boundary is especially useful: &lt;strong&gt;annotations do not contribute to rendered media duration&lt;/strong&gt;. Adding a planning marker at 90 seconds to a 60-second edit must not produce another 30 seconds of video.&lt;/p&gt;

&lt;p&gt;Chapter markers also do not automatically become on-screen titles or MP4 chapters. They remain editable project annotations. A marker-only task can therefore finish by writing a new project without re-encoding unchanged media.&lt;/p&gt;

&lt;p&gt;The tradeoff is explicit timing: annotations stay at their absolute project positions during trims, reordering, and ripple edits. If they need to follow moved content, the edit plan must update them too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the React interaction compact&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An always-visible annotation lane takes space away from media tracks. The default view now merges compact flags into the ruler. A toolbar chevron expands a separate lane with titles and range spans when more detail is useful.&lt;/p&gt;

&lt;p&gt;Pressing &lt;code&gt;M&lt;/code&gt; adds a marker at the playhead; &lt;code&gt;Shift + M&lt;/code&gt; opens the manager. The marker UI is localized in all 13 supported interface languages.&lt;/p&gt;

&lt;p&gt;During a drag, React holds a temporary preview. Releasing the pointer commits the edit; Escape or a canceled gesture discards the preview. This separates continuous pointer feedback from the final project mutation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure snapping in pixels, then convert to time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A fixed temporal threshold such as 0.2 seconds feels very different at different zoom levels. The implementation converts a 10-pixel tolerance into project seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thresholdSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;railWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;timelineDuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;railWidth&lt;/code&gt; is the timeline rail width and &lt;code&gt;timelineDuration&lt;/code&gt; is its corresponding time span. This keeps the screen-space snapping distance approximately consistent as the timeline zoom changes.&lt;/p&gt;

&lt;p&gt;Markers reuse the shared snapping targets: the playhead, clip boundaries, and other marker edges. The editor shows a common alignment guide, and holding Alt temporarily bypasses snapping.&lt;/p&gt;

&lt;p&gt;Ranges need an additional invariant: moving a range must preserve its length. If the end of a three-second range snaps to 12 seconds, its start must become 9 seconds.&lt;/p&gt;

&lt;p&gt;The core calculation, simplified, is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;movingEdge&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextStart&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual implementation checks both edges and chooses the nearer valid snap candidate. Resizing an edge is handled separately from moving the whole range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expose one command engine through CLI and MCP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent-facing architecture is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent Skill: workflow, timing evidence, verification
                         |
CLI / MCP: structured operation entry points
                         |
Shared command engine: validation, edits, semantic diff
                         |
A new .timeline project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP adapter invokes the existing CLI runner. It does not maintain its own marker reducers. Fixing a validation rule therefore fixes both entry points.&lt;/p&gt;

&lt;p&gt;From the repository root, an agent can inspect the project and its annotations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.inspect /projects/input.timeline
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; marker.inspect /projects/input.timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP exposes the read-only &lt;code&gt;timeline_marker_inspect&lt;/code&gt; tool. Writes use &lt;code&gt;marker.add&lt;/code&gt;, &lt;code&gt;marker.update&lt;/code&gt;, and &lt;code&gt;marker.delete&lt;/code&gt; through the existing project diff/apply workflow.&lt;/p&gt;

&lt;p&gt;For example, suppose inspection reports revision &lt;code&gt;0&lt;/code&gt; and a &lt;code&gt;product-review&lt;/code&gt; range spanning 5–8 seconds. This plan moves it to 9 seconds and adds a note at 11 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/projects/input.timeline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"baseRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"move-product-v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"marker.update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"markerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product-review"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add-ending-note-v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"marker.add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"markerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ending-note"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"markerType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ending rhythm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reviewer request: leave a little more breathing room."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rose"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/projects/output-marked.timeline"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paths, revision, IDs, and review request are illustrative. A real plan must use inspected values and actual feedback.&lt;/p&gt;

&lt;p&gt;Two distinctions matter: &lt;code&gt;id&lt;/code&gt; identifies an operation for retry handling, while &lt;code&gt;markerId&lt;/code&gt; identifies the annotation. Likewise, &lt;code&gt;type&lt;/code&gt; selects the command, while &lt;code&gt;markerType&lt;/code&gt; selects the annotation kind.&lt;/p&gt;

&lt;p&gt;Updating only the time of a range preserves its length. The example moves 5–8 seconds to 9–12 seconds. An explicit &lt;code&gt;endTime&lt;/code&gt; lets the plan change the end instead.&lt;/p&gt;

&lt;p&gt;Save the plan as &lt;code&gt;/projects/markers-plan.json&lt;/code&gt;, validate its structure, and preview the semantic diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node skills/edit-timeline-studio/scripts/validate_edit_plan.mjs /projects/markers-plan.json
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.diff /projects/markers-plan.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After reviewing the result, apply the same plan and inspect the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.run /projects/markers-plan.json
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; marker.inspect /projects/output-marked.timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diff reports additions, removals, and modifications under &lt;code&gt;changes.markers&lt;/code&gt;, including before/after values. A marker-only edit should not unexpectedly modify a media track.&lt;/p&gt;

&lt;p&gt;The Skill instructs the agent to inspect, review the diff, apply, and inspect again. The engine enforces concrete constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A mismatched revision returns &lt;code&gt;REVISION_CONFLICT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Previously applied operation IDs are not applied again.&lt;/li&gt;
&lt;li&gt;A failed operation rejects the batch without writing a partially edited archive.&lt;/li&gt;
&lt;li&gt;Output must go to a new path; input files and existing outputs cannot be overwritten.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A revision conflict requires a fresh inspection and a reconsidered plan. Merely replacing the revision number would skip the reason for having that check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tricky bugs were identity and floating-point boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider imported annotations with three identical IDs: &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, and &lt;code&gt;x&lt;/code&gt;. Inspection normalizes them to &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;x-2&lt;/code&gt;, and &lt;code&gt;x-3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If normalization runs after every individual operation, deleting the first annotation can change the identities of the remaining entries. A subsequent update to &lt;code&gt;x-2&lt;/code&gt; may then hit the wrong annotation.&lt;/p&gt;

&lt;p&gt;The fix was to normalize once on the transaction copy before marker writes, then resolve every operation against those fixed IDs. Duplicate-ID suffixes also reserve space within the 160-character limit so generated IDs remain valid.&lt;/p&gt;

&lt;p&gt;The second bug involved a one-millisecond range near 1,000 seconds. Subtracting its endpoints can produce a value slightly below &lt;code&gt;0.001&lt;/code&gt;. A strict comparison can reject a valid move toward zero. The implementation now accounts for floating-point error at the minimum range boundary while continuing to reject genuinely invalid intervals.&lt;/p&gt;

&lt;p&gt;Both bugs were easy to miss when testing a single ordinary drag. Batch operations and imported data made them visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An agent still needs evidence for its timestamps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;UI snapping is a pointer interaction. CLI and MCP commands take exact seconds; they do not apply pixel-distance snapping.&lt;/p&gt;

&lt;p&gt;For an event in source footage at constant playback speed, the mapping is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projectTime = clipStart
            + (sourceTime - sourceStart) / playbackRate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a speed curve, the corresponding source-time mapping is required. Dividing by an average speed can place the cue incorrectly.&lt;/p&gt;

&lt;p&gt;Music cues have a similar boundary. The marker commands store annotations; they do not perform automatic beat detection. An agent needs supplied cue times or a verified analysis before writing a beat grid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was verified&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The checks covered real CLI and MCP calls, plus an independent agent following the Skill to move a range from 5–8 to 9–12 seconds and add a Unicode note at 11 seconds.&lt;/p&gt;

&lt;p&gt;Verification checked the range length, unrelated annotations, the original archive hash, embedded media bytes, media duration, and the unchanged rendering plan for annotation-only edits. Skill validation, type checking, the production build, and GitHub CI passed. Existing lint warnings remain.&lt;/p&gt;

&lt;p&gt;The result is a project that can carry editorial intent between people and agents. A human can leave a review note, an agent can act on its exact location, and the next person can inspect the resulting project and its annotations.&lt;/p&gt;

&lt;p&gt;If you want to try the flags and range snapping, open the &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;Timeline Studio editor&lt;/a&gt;. If you are building a React editor or exposing a creative tool through MCP, the &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and &lt;a href="https://github.com/MartinDelophy/ai-video-editor/blob/v1.0.7/skills/edit-timeline-studio/references/timeline-markers.md" rel="noopener noreferrer"&gt;marker workflow reference&lt;/a&gt; contain the implementation and command contract. Stars help you follow updates; reproducible issues and design feedback are welcome.&lt;/p&gt;

&lt;p&gt;To install Skill v1.0.7 for Codex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh skill &lt;span class="nb"&gt;install &lt;/span&gt;MartinDelophy/ai-video-editor edit-timeline-studio &lt;span class="nt"&gt;--pin&lt;/span&gt; v1.0.7 &lt;span class="nt"&gt;--agent&lt;/span&gt; codex &lt;span class="nt"&gt;--scope&lt;/span&gt; user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Skill installs the workflow. Running local commands still requires a Timeline Studio checkout with its Node dependencies, and the MCP adapter should run from that checkout. See the &lt;a href="https://github.com/MartinDelophy/ai-video-editor/releases/tag/v1.0.7" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; for the published version.&lt;/p&gt;

</description>
      <category>react</category>
      <category>mcp</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>What It Takes to Run an AI Video Editor Locally in the Browser</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:33:27 +0000</pubDate>
      <link>https://dev.to/martindelophy/what-it-takes-to-run-an-ai-video-editor-locally-in-the-browser-103g</link>
      <guid>https://dev.to/martindelophy/what-it-takes-to-run-an-ai-video-editor-locally-in-the-browser-103g</guid>
      <description>&lt;p&gt;Most AI video tools begin with an upload button.&lt;/p&gt;

&lt;p&gt;That is convenient for the application developer, but it changes the product for the user. Raw footage can be large, personal, slow to transfer, and expensive to process repeatedly. For a video editor, uploading is not a one-time action either: every trim, caption, effect, and export can create another round trip.&lt;/p&gt;

&lt;p&gt;I wanted to explore a different constraint: &lt;strong&gt;how much of an AI video-editing workflow can stay inside a modern browser?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question became &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;, an open-source, local-first AI video editor with a multi-track timeline, captions, voiceovers, visual effects, browser AI tools, and offline export.&lt;/p&gt;

&lt;p&gt;The interesting part was not putting a familiar editor UI on a webpage. It was making media, AI inference, model delivery, timeline state, preview, and export behave like one coherent system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local-first is an architectural constraint
&lt;/h2&gt;

&lt;p&gt;For supported workflows, project media is processed in the browser instead of being uploaded to an editing backend. This changes several engineering decisions at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inference needs browser-compatible runtimes;&lt;/li&gt;
&lt;li&gt;model downloads must be lazy, resumable, cached, and versioned;&lt;/li&gt;
&lt;li&gt;timeline operations need deterministic state rather than server-side session magic;&lt;/li&gt;
&lt;li&gt;preview must stay responsive while export remains reproducible;&lt;/li&gt;
&lt;li&gt;failures need to explain browser or hardware limitations without hiding them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebGPU is the main compute path for heavier models. WASM remains important for operations where it is more stable or broadly compatible. ONNX gives the project a practical interchange format, but converting a model is only the beginning. A model that technically loads can still be unusable because of memory pressure, unsupported operators, slow graph initialization, or inconsistent numerical output across execution providers.&lt;/p&gt;

&lt;p&gt;The result is a mixed runtime rather than a single “run everything on WebGPU” switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model delivery is part of the product
&lt;/h2&gt;

&lt;p&gt;A local model still has to reach the user once.&lt;/p&gt;

&lt;p&gt;Timeline Studio lazy-loads AI artifacts only when a feature is first used. The service worker owns the persistent model cache, while later runs reuse the cached files. Models are pinned to known revisions so an upstream change cannot silently alter an editing workflow.&lt;/p&gt;

&lt;p&gt;The project also mirrors model artifacts across Hugging Face and ModelScope. Chinese and domestic sessions prefer ModelScope; other sessions prefer Hugging Face. If the preferred provider is unavailable, the application can fall back to the other mirror.&lt;/p&gt;

&lt;p&gt;The important detail is that both providers map to one cache identity. A fallback should not create a second full copy of the same model on the user's device.&lt;/p&gt;

&lt;p&gt;This sounds like infrastructure work—and it is—but it directly affects the editing experience. “Generate music” should not look like a fresh model download every time. A cache miss should not become a mysterious network exception. A pinned model should carry its license and source notes with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A responsive preview and a deterministic export are different jobs
&lt;/h2&gt;

&lt;p&gt;Video editors need immediate feedback. Native media playback and a live canvas make trimming and positioning feel responsive, but they are not enough for final export.&lt;/p&gt;

&lt;p&gt;Real-time playback can vary with decoding speed, dropped frames, tab scheduling, and device load. If final rendering simply records whatever happened on screen, two exports of the same project can drift.&lt;/p&gt;

&lt;p&gt;Timeline Studio therefore treats the timeline as declarative project state and uses a separate offline rendering path for export. Clips, source trims, transforms, captions, overlays, audio, effects, and keyframes are evaluated against explicit timestamps. WebCodecs is used for MP4/WebM composition where available, with a compatibility path for browsers that need it.&lt;/p&gt;

&lt;p&gt;The two paths have different performance goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preview:&lt;/strong&gt; respond quickly enough to edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export:&lt;/strong&gt; evaluate the same project state consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They still need shared geometry, timing, interpolation, and effect definitions. Otherwise the editor develops the most damaging kind of bug: a preview that looks correct and an export that does not.&lt;/p&gt;

&lt;p&gt;This is why small visual features can require surprisingly deep work. A timed ripple effect, for example, is not just a CSS animation. Its seeded placement, beat timing, grayscale-to-color reveal, wave propagation, and decay must all be derived from the same deterministic parameters in both preview and export.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI output should become editable media
&lt;/h2&gt;

&lt;p&gt;Another design decision was to avoid treating AI as a collection of isolated demo buttons.&lt;/p&gt;

&lt;p&gt;Generated or analyzed results should return to the editing model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatic captions become timed caption segments;&lt;/li&gt;
&lt;li&gt;generated voiceovers become audio clips;&lt;/li&gt;
&lt;li&gt;separated vocals and instrumentals become independent audio tracks;&lt;/li&gt;
&lt;li&gt;AI music is added to the user's assets instead of silently replacing the timeline;&lt;/li&gt;
&lt;li&gt;repair and subject-isolation results remain reviewable and editable;&lt;/li&gt;
&lt;li&gt;portable .timeline projects preserve the composition as the source of truth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser currently supports workflows including multilingual voice generation, Whisper-based captions, local AI music, smart framing, object or watermark repair, restoration, vocal separation, and talking-avatar generation. Not every device will run every large model comfortably, so the UI has to expose setup progress, cancellation, caching, and compatibility honestly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did not fit the local-first story
&lt;/h2&gt;

&lt;p&gt;There is a temptation to claim that moving inference into the browser removes every tradeoff. It does not.&lt;/p&gt;

&lt;p&gt;The first model download may be substantial. GPU memory varies widely. WebGPU behavior still differs across browser and driver combinations. Mobile devices require more conservative paths. Some models become slower or less reliable after conversion, even when their quality looked good in the original research environment.&lt;/p&gt;

&lt;p&gt;Local-first also does not mean “offline from the first visit.” The application shell and requested models must be downloaded before they can be cached. A more accurate promise is: &lt;strong&gt;supported editing workflows can run without uploading the user's project media to an editing backend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That narrower statement is less dramatic, but it is testable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source it?
&lt;/h2&gt;

&lt;p&gt;Browser media work sits at the intersection of WebCodecs, WebGPU, ONNX, audio processing, timeline UX, caching, and export. Many of the hardest problems are integration problems, and they benefit from reproducible bug reports and implementations that can be inspected.&lt;/p&gt;

&lt;p&gt;Timeline Studio is available under the MIT License on &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The repository includes the editor, multilingual documentation, portable project tooling, and an agent skill for inspecting, editing, diffing, and rendering timeline projects.&lt;/p&gt;

&lt;p&gt;There is still plenty to improve: broader headless render parity, more reliable cross-device export, a larger versioned command registry, and better recovery when browser AI reaches hardware limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching today
&lt;/h2&gt;

&lt;p&gt;Timeline Studio is launching on &lt;a href="https://www.producthunt.com/products/timeline-studio-2?launch=timeline-studio-2" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt; today.&lt;/p&gt;

&lt;p&gt;If local-first creative software interests you, try the editor and share your honest feedback. I am especially interested in where the browser-local model feels clear, where setup is confusing, and which real editing workflow breaks first.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Best Feature I Shipped Was Removing Friction: Rebuilding the UX of a Browser Video Editor</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Mon, 24 Aug 2026 08:30:27 +0000</pubDate>
      <link>https://dev.to/martindelophy/the-best-feature-i-shipped-was-removing-friction-rebuilding-the-ux-of-a-browser-video-editor-33k</link>
      <guid>https://dev.to/martindelophy/the-best-feature-i-shipped-was-removing-friction-rebuilding-the-ux-of-a-browser-video-editor-33k</guid>
      <description>&lt;p&gt;When I first shared &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;, it had around 80 GitHub stars.&lt;/p&gt;

&lt;p&gt;The technical premise was already there: a local-first video editor that runs in the browser, with a multi-track timeline, voiceovers, captions, effects, and offline export.&lt;/p&gt;

&lt;p&gt;The user experience was not.&lt;/p&gt;

&lt;p&gt;Some early feedback was blunt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Interesting project, but the interface feels clunky.&lt;/p&gt;

&lt;p&gt;There are a lot of features, but some interactions feel unintuitive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That feedback was fair. I had spent too much time proving that browser video editing was possible and not enough time asking whether ordinary editing actions behaved the way people expected.&lt;/p&gt;

&lt;p&gt;The project has since passed 500 GitHub stars. The number is nice, but the more meaningful milestone was going back and fixing the small frustrations that made the entire editor feel harder than it needed to be.&lt;/p&gt;

&lt;p&gt;This is a post about those fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  A feature can work and still be wrong
&lt;/h2&gt;

&lt;p&gt;The early version had split, zoom, scrolling, track locking, audio separation, mobile controls, and export settings.&lt;/p&gt;

&lt;p&gt;On paper, those features existed.&lt;/p&gt;

&lt;p&gt;In practice, they often exposed the implementation instead of matching the user's mental model.&lt;/p&gt;

&lt;p&gt;For example, the split action originally required an explicitly selected clip. A user could place the playhead in the middle of the main video, click the scissors button, and still be told to select a clip first.&lt;/p&gt;

&lt;p&gt;The code was consistent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;selectedClip -&amp;gt; split(selectedClip, playheadTime)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interaction was not.&lt;/p&gt;

&lt;p&gt;The user was looking at the main visual under the playhead. They had already communicated enough intent.&lt;/p&gt;

&lt;p&gt;The current rule is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if a clip is explicitly selected:
    split that clip
else:
    find the main visual under the playhead
    select and split it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicit selections are still respected, but the common action now has a useful default.&lt;/p&gt;

&lt;p&gt;This change added no new capability. It simply removed a question the editor should never have asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lock the meaning of a continuous gesture
&lt;/h2&gt;

&lt;p&gt;Timeline editors usually need several wheel behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vertical track scrolling&lt;/li&gt;
&lt;li&gt;horizontal timeline panning&lt;/li&gt;
&lt;li&gt;timeline zoom&lt;/li&gt;
&lt;li&gt;Shift-modified panning&lt;/li&gt;
&lt;li&gt;Ctrl/Cmd wheel zoom&lt;/li&gt;
&lt;li&gt;trackpad pinch zoom&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My initial implementation kept resolving the wheel behavior from whatever happened to be under the pointer.&lt;/p&gt;

&lt;p&gt;That sounds reasonable until the timeline moves.&lt;/p&gt;

&lt;p&gt;Imagine starting a vertical scroll over empty timeline space. As the tracks move, a clip slides under the stationary pointer. If the editor resolves the target again, the gesture can suddenly change from scrolling tracks to zooming the timeline.&lt;/p&gt;

&lt;p&gt;From the user's perspective, the editor has changed its mind halfway through an action.&lt;/p&gt;

&lt;p&gt;The fix was to treat a continuous wheel sequence as one gesture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gestureTarget = hitTest(pointerPositionAtStart)
gestureMode = resolveMode(gestureTarget, modifierKeys)

while the gesture continues:
    keep gestureMode stable

after a short pause:
    allow the next gesture to resolve again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting behavior is predictable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start over empty timeline space to scroll track lanes&lt;/li&gt;
&lt;li&gt;start over a clip to zoom the timeline&lt;/li&gt;
&lt;li&gt;use Shift for horizontal panning&lt;/li&gt;
&lt;li&gt;use Ctrl/Cmd or pinch for explicit zoom&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is not the exact mapping. It is that the mapping does not mutate while the user is still performing the same physical gesture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Users manipulate clips, not source asset IDs
&lt;/h2&gt;

&lt;p&gt;A split video often produces several timeline clips that reference the same source file.&lt;/p&gt;

&lt;p&gt;Internally, that might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sourceAssetId: video-001

clip A -&amp;gt; source range 0s–5s
clip B -&amp;gt; source range 5s–12s
clip C -&amp;gt; source range 12s–20s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An early version of "Separate audio" relied too heavily on the shared source asset ID. That made whole-asset matching convenient, but it violated what the user had actually asked for.&lt;/p&gt;

&lt;p&gt;When the command comes from clip B's context menu, the expected result is the audio for 5s–12s—not new mappings for every sibling clip backed by &lt;code&gt;video-001&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The current behavior operates on the exact timeline segment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preserve its source trim&lt;/li&gt;
&lt;li&gt;preserve its playback speed&lt;/li&gt;
&lt;li&gt;place the extracted audio on an audio lane&lt;/li&gt;
&lt;li&gt;mute the corresponding picture-in-picture clip to prevent doubled sound&lt;/li&gt;
&lt;li&gt;select and reveal the resulting audio&lt;/li&gt;
&lt;li&gt;leave untouched sibling clips alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whole-asset matching still makes sense when an unsplit source asset is intentionally dragged onto a source-audio track. It does not make sense for a clip-scoped menu action.&lt;/p&gt;

&lt;p&gt;The lesson was simple: internal identity is not the same thing as interaction scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic layout should not rearrange confirmed work
&lt;/h2&gt;

&lt;p&gt;Audio lane placement caused a similar problem.&lt;/p&gt;

&lt;p&gt;When a new voice clip overlapped existing audio, an automatic placement algorithm could move an existing clip to another lane. The layout became technically valid, but the user's previously arranged work changed without permission.&lt;/p&gt;

&lt;p&gt;Now existing clips stay where they are.&lt;/p&gt;

&lt;p&gt;New voice or audio clips find another available lane, or a new ordinary audio lane is materialized when necessary. AI music always routes to the dedicated music track. Split audio can still be moved vertically between normal audio lanes, but adding something new never displaces something already confirmed.&lt;/p&gt;

&lt;p&gt;Automation should solve the new placement problem, not reopen old decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the primary visual in view
&lt;/h2&gt;

&lt;p&gt;Once a project contains overlays, captions, source audio, voiceovers, and music, vertical scrolling becomes unavoidable.&lt;/p&gt;

&lt;p&gt;Letting every track scroll together meant the main visual could disappear while the user worked on lower lanes. That removed the most important reference point in the composition.&lt;/p&gt;

&lt;p&gt;The main visual track is now pinned directly below the ruler. Overlay and lower tracks scroll underneath it.&lt;/p&gt;

&lt;p&gt;The persistent scrollbar chrome is hidden, but both horizontal and vertical scrolling remain functional. The pinned track's lower edge also acts as the upper boundary for vertical drag auto-scroll, so cross-lane moves still behave correctly.&lt;/p&gt;

&lt;p&gt;This required more than &lt;code&gt;position: sticky&lt;/code&gt;. Pointer hit testing, drag targets, auto-scroll boundaries, and main-to-overlay or overlay-to-main moves all had to use the same geometry.&lt;/p&gt;

&lt;p&gt;The visible result is much less dramatic: the main picture simply stops getting lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile is not desktop with smaller buttons
&lt;/h2&gt;

&lt;p&gt;The first mobile layout was essentially the desktop editor compressed into a narrow viewport.&lt;/p&gt;

&lt;p&gt;It technically contained the same features. It was also exhausting to use.&lt;/p&gt;

&lt;p&gt;The redesigned mobile workspace follows a different hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preview first&lt;/li&gt;
&lt;li&gt;timeline immediately below it&lt;/li&gt;
&lt;li&gt;a centered playhead with the timeline moving around it&lt;/li&gt;
&lt;li&gt;clip-specific actions after selection&lt;/li&gt;
&lt;li&gt;properties inside a focused bottom drawer&lt;/li&gt;
&lt;li&gt;touch-sized controls&lt;/li&gt;
&lt;li&gt;no desktop keyboard-shortcut guide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Desktop and mobile still edit the same project model. They do not need to expose that model through the same interface.&lt;/p&gt;

&lt;p&gt;Responsive design is not preserving every panel at every width. It is preserving the user's task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small state mismatches create large trust problems
&lt;/h2&gt;

&lt;p&gt;Several fixes were almost embarrassingly small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking the Export button a second time now closes the export popover.&lt;/li&gt;
&lt;li&gt;Icon-only timeline controls have localized tooltips on hover and keyboard focus.&lt;/li&gt;
&lt;li&gt;Locked tracks retain a subdued treatment, but their clips no longer gain a dashed border that looks like selection.&lt;/li&gt;
&lt;li&gt;"Analyze person" and "Analyze object" use the primary action style whenever they are actionable; the neutral style is reserved for "Cancel analysis."&lt;/li&gt;
&lt;li&gt;Generated music and converted voices go to the asset library instead of silently modifying the timeline.&lt;/li&gt;
&lt;li&gt;Repeated voice generations append after the current voiceover end instead of stacking at 0 seconds or an unchanged playhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these belongs in an impressive model benchmark.&lt;/p&gt;

&lt;p&gt;Together, they answer a more important question: can the user predict what will happen after the next click?&lt;/p&gt;

&lt;h2&gt;
  
  
  The product changed when the question changed
&lt;/h2&gt;

&lt;p&gt;At the beginning, I kept asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can this run in a browser?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Can WebCodecs export the composition? Can WebGPU run the model? Can the timeline support another track type? Can an agent modify the project?&lt;/p&gt;

&lt;p&gt;Those were useful questions, but they produced a technology demonstration.&lt;/p&gt;

&lt;p&gt;The editor started becoming a product when the question changed to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does the user reasonably expect to happen here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question led to fewer interruptions, stable gestures, clip-scoped actions, conservative automation, clearer state, and a mobile interface built around touch rather than CSS breakpoints.&lt;/p&gt;

&lt;p&gt;Timeline Studio is still evolving. It combines a real multi-track editor with local-first browser AI, portable &lt;code&gt;.timeline&lt;/code&gt; projects, and deterministic offline export. There are still plenty of rough edges to find.&lt;/p&gt;

&lt;p&gt;But if you tried an early version and found the UI clunky, I would genuinely like to know whether the same problems still stand—and what remains confusing today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;https://github.com/MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live editor:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;https://video-editor.ai-creator.top/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most useful feedback is not "add another AI model."&lt;/p&gt;

&lt;p&gt;It is: "I expected this action to do X, but it did Y."&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>AI Video Generation Is Expensive—So I Built a 15-Second Remix Workflow</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Sat, 22 Aug 2026 09:11:55 +0000</pubDate>
      <link>https://dev.to/martindelophy/ai-video-generation-is-expensive-so-i-built-a-15-second-remix-workflow-1ib7</link>
      <guid>https://dev.to/martindelophy/ai-video-generation-is-expensive-so-i-built-a-15-second-remix-workflow-1ib7</guid>
      <description>&lt;p&gt;Generating AI video looks effortless in a demo: write a prompt, wait a minute, and receive a cinematic shot.&lt;/p&gt;

&lt;p&gt;In practice, the expensive part is not only the subscription or the credits. It is the uncertainty. A usable five-second clip may require several generations because the face changes, the motion breaks, the composition drifts, or the shot simply does not fit the rest of the sequence.&lt;/p&gt;

&lt;p&gt;That led me to a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if creators did not have to generate every frame from scratch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tested a more practical workflow: start with video material I am authorized to use, analyze it for high-impact moments, rebuild the pacing, replace the original audio, and keep the result as an editable timeline instead of a disposable render.&lt;/p&gt;

&lt;p&gt;The tool behind the experiment is &lt;strong&gt;Timeline Studio&lt;/strong&gt;, an open-source, browser-based video editor:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub: &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl7tjvd1yjht8y6o7472r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl7tjvd1yjht8y6o7472r.png" alt="A cinematic frame selected for the final 15-second edit" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The experiment
&lt;/h2&gt;

&lt;p&gt;The input was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one source video of roughly two minutes;&lt;/li&gt;
&lt;li&gt;one music track of more than five minutes;&lt;/li&gt;
&lt;li&gt;a second video containing a specific three-second ending shot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The target was a &lt;strong&gt;15-second short-form edit&lt;/strong&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple memory-like fragments;&lt;/li&gt;
&lt;li&gt;a clear emotional build;&lt;/li&gt;
&lt;li&gt;a transition in the middle;&lt;/li&gt;
&lt;li&gt;a different final shot;&lt;/li&gt;
&lt;li&gt;the original source audio removed;&lt;/li&gt;
&lt;li&gt;a musical highlight near &lt;strong&gt;1:20&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;a real ending, rather than an abrupt cutoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final structure looked like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Editorial function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0–3s&lt;/td&gt;
&lt;td&gt;Establish the character and atmosphere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3–7s&lt;/td&gt;
&lt;td&gt;Build a fragmented memory rhythm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Around 7s&lt;/td&gt;
&lt;td&gt;Use a short flash transition as an emotional hinge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7–12s&lt;/td&gt;
&lt;td&gt;Increase energy while protecting readable hero frames&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12–15s&lt;/td&gt;
&lt;td&gt;Switch to the requested ending shot and fade to black&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The final three seconds came from &lt;strong&gt;2:48–2:51&lt;/strong&gt; of the second source. Its embedded audio was muted, so the entire edit used only one clean music track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why “highlight detection” is not random trimming
&lt;/h2&gt;

&lt;p&gt;A common automation mistake is to cut a long video into equal intervals and keep the most active sections.&lt;/p&gt;

&lt;p&gt;That produces motion, but not necessarily meaning.&lt;/p&gt;

&lt;p&gt;For this experiment, candidate moments were evaluated with several kinds of evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;audio-energy change;&lt;/li&gt;
&lt;li&gt;subject-aware motion and frame difference;&lt;/li&gt;
&lt;li&gt;shot-change confidence;&lt;/li&gt;
&lt;li&gt;expression change for a tracked face;&lt;/li&gt;
&lt;li&gt;clarity around the main subject.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A practical scoring model can combine these signals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;highlight_score =
    0.30 * audio_energy_change
  + 0.25 * subject_motion
  + 0.20 * shot_change_confidence
  + 0.15 * expression_change
  + 0.10 * subject_clarity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the score is only a candidate signal. It should not override continuity, intentional stillness, anticipation, or the most important result frame.&lt;/p&gt;

&lt;p&gt;After saliency analysis, each retained beat still needs an editorial role: setup, rise, pre-impact, peak, aftershock, or bridge. Without that hierarchy, every shot receives equal weight and the montage feels flat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the musical high point
&lt;/h2&gt;

&lt;p&gt;The user requested the highlight near 1:20, so I searched around that region instead of blindly cutting from exactly 80 seconds.&lt;/p&gt;

&lt;p&gt;The selected range was approximately &lt;strong&gt;80.5–95.5 seconds&lt;/strong&gt;. That gave the 15-second edit a more natural musical phrase and enough room for a controlled ending.&lt;/p&gt;

&lt;p&gt;A simplified FFmpeg extraction looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-ss&lt;/span&gt; 80.5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; 15 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; input.aac &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="s2"&gt;"afade=t=in:st=0:d=0.15,afade=t=out:st=14.3:d=0.7"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt;:a aac &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-b&lt;/span&gt;:a 192k &lt;span class="se"&gt;\&lt;/span&gt;
  music-highlight.m4a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does four important things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;starts close to the requested musical moment;&lt;/li&gt;
&lt;li&gt;limits the result to exactly 15 seconds;&lt;/li&gt;
&lt;li&gt;avoids a hard entrance;&lt;/li&gt;
&lt;li&gt;fades the final 0.7 seconds so the music and picture resolve together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The loudest passage is not automatically the best passage. A useful music highlight also needs a readable beat, a complete phrase, an emotional lift, and a point where the ending can land.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the “memory fragment” feeling
&lt;/h2&gt;

&lt;p&gt;A memory montage is not just a color preset.&lt;/p&gt;

&lt;p&gt;The feeling came from combining several editorial decisions:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Short, non-contiguous fragments
&lt;/h3&gt;

&lt;p&gt;The first 12 seconds use eight short clips instead of one continuous excerpt. The clips come from different source times and are reorganized around emotional progression.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A restrained flash transition
&lt;/h3&gt;

&lt;p&gt;A brief flash near the middle separates two emotional phases. It is intentionally short and placed away from the decisive frame.&lt;/p&gt;

&lt;p&gt;Transitions should clarify structure. If an effect hides the action viewers need to see, it is working against the edit.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A protected ending
&lt;/h3&gt;

&lt;p&gt;The requested ending shot occupies exactly 12–15 seconds. The final 0.7 seconds fade to black, giving the piece a deliberate closing gesture.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. One coherent audio bed
&lt;/h3&gt;

&lt;p&gt;All embedded source audio is muted. Keeping only the selected music highlight avoids doubled sound and makes later remixing predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow stayed editable
&lt;/h2&gt;

&lt;p&gt;A rendered MP4 is only the delivery artifact. The actual source of truth is the editable timeline.&lt;/p&gt;

&lt;p&gt;The project contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eight memory fragments;&lt;/li&gt;
&lt;li&gt;one three-second ending clip;&lt;/li&gt;
&lt;li&gt;source-time trims for every segment;&lt;/li&gt;
&lt;li&gt;a middle flash transition;&lt;/li&gt;
&lt;li&gt;a final fade;&lt;/li&gt;
&lt;li&gt;one music segment;&lt;/li&gt;
&lt;li&gt;muted embedded audio;&lt;/li&gt;
&lt;li&gt;normalized output dimensions and frame rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because editing is iterative. If the ending needs to start 300 milliseconds earlier, I should not have to reconstruct a giant filter graph or repeat the full analysis.&lt;/p&gt;

&lt;p&gt;Timeline Studio stores the edit as a &lt;code&gt;.timeline&lt;/code&gt; project, so clips, timing, transitions, audio, and effects can continue to be adjusted visually or through automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjk563euuezkov32zedbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjk563euuezkov32zedbn.png" alt="Timeline Studio production log showing the ending replacement, frame-rate normalization, muted source audio, and final fade" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshot above captures part of the actual production process. The ending source ran at 30 fps, while the main sequence used 29 fps, so the render pipeline normalized the final output and fitted the new shot to the project resolution without reintroducing its original sound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical validation
&lt;/h2&gt;

&lt;p&gt;A video is not finished when the encoder exits successfully.&lt;/p&gt;

&lt;p&gt;For the final result, I verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duration: &lt;strong&gt;15.000 seconds&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;resolution: &lt;strong&gt;1906 × 1080&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;frame rate: &lt;strong&gt;29 fps&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;total frames: &lt;strong&gt;435&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;video codec: &lt;strong&gt;H.264&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;audio codec: &lt;strong&gt;AAC stereo&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;full decode: successful;&lt;/li&gt;
&lt;li&gt;the cut at 12 seconds;&lt;/li&gt;
&lt;li&gt;the fade between 14.3 and 15 seconds;&lt;/li&gt;
&lt;li&gt;no embedded audio leaking from the ending clip.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basic metadata inspection can be automated with &lt;code&gt;ffprobe&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffprobe &lt;span class="nt"&gt;-v&lt;/span&gt; error &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-show_entries&lt;/span&gt; &lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;duration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-show_entries&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index,codec_name,width,height,r_frame_rate,channels &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-of&lt;/span&gt; json output.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also recommend decoding the complete output once instead of trusting container metadata alone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-v&lt;/span&gt; error &lt;span class="nt"&gt;-i&lt;/span&gt; output.mp4 &lt;span class="nt"&gt;-f&lt;/span&gt; null -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That catches broken frames and stream errors that a successful export message may miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this can be cheaper than generation-only production
&lt;/h2&gt;

&lt;p&gt;This workflow does not replace generative video. It changes where generation is used.&lt;/p&gt;

&lt;p&gt;Generation is excellent when a story requires a shot that does not exist. But when usable footage already exists, repeatedly generating near-duplicates is often a poor use of time and credits.&lt;/p&gt;

&lt;p&gt;A hybrid pipeline can be more efficient:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;reuse authorized footage where it already works;&lt;/li&gt;
&lt;li&gt;analyze and restructure it;&lt;/li&gt;
&lt;li&gt;generate only the missing shots;&lt;/li&gt;
&lt;li&gt;import those shots as editable source media;&lt;/li&gt;
&lt;li&gt;validate the finished sequence as one coherent piece.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scarce skill is gradually moving from “Can I generate an image?” to “Can I select, structure, pace, and finish a story?”&lt;/p&gt;

&lt;h2&gt;
  
  
  A necessary note about copyright
&lt;/h2&gt;

&lt;p&gt;“Remix” does not mean downloading and reposting somebody else’s work.&lt;/p&gt;

&lt;p&gt;Use footage that you created, generated, licensed, purchased, or received permission to modify. Check whether the license permits derivatives and commercial use. Keep provenance and watermarks when required, and verify music, likeness, and brand rights separately.&lt;/p&gt;

&lt;p&gt;Good second creation adds a new structure, viewpoint, explanation, or experience. Removing a watermark and uploading the same content is not a creative workflow—and may violate both rights and platform policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Timeline Studio
&lt;/h2&gt;

&lt;p&gt;Timeline Studio is my attempt to connect local media analysis, browser-based editing, and deterministic rendering in one workflow.&lt;/p&gt;

&lt;p&gt;The project is useful for experiments involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;content-aware highlight extraction;&lt;/li&gt;
&lt;li&gt;short-form montage generation;&lt;/li&gt;
&lt;li&gt;music highlight selection;&lt;/li&gt;
&lt;li&gt;editable transitions and effects;&lt;/li&gt;
&lt;li&gt;caption and voice workflows;&lt;/li&gt;
&lt;li&gt;AI-generated footage as source media;&lt;/li&gt;
&lt;li&gt;local-first media processing;&lt;/li&gt;
&lt;li&gt;reusable &lt;code&gt;.timeline&lt;/code&gt; projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this direction is useful to you, take a look at the repository:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;https://github.com/MartinDelophy/ai-video-editor&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Issues, feedback, and contributions are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;AI video generation lowers the cost of producing footage, but more footage does not automatically create better stories.&lt;/p&gt;

&lt;p&gt;Generation determines what material is available. Editing determines what the audience experiences.&lt;/p&gt;

&lt;p&gt;For independent creators, the sustainable advantage may not be generating everything from zero. It may be building a repeatable system that knows what to keep, what to cut, when to peak, and how to end.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AI Video Editing Should Be More Than Auto-Cutting: Teaching an Agent Professional Post-Production</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Wed, 19 Aug 2026 08:02:37 +0000</pubDate>
      <link>https://dev.to/martindelophy/ai-video-editing-should-be-more-than-auto-cutting-teaching-an-agent-professional-post-production-4dke</link>
      <guid>https://dev.to/martindelophy/ai-video-editing-should-be-more-than-auto-cutting-teaching-an-agent-professional-post-production-4dke</guid>
      <description>&lt;p&gt;Most AI video editors can already select clips, remove silence, generate captions, add music, and cut on the beat.&lt;/p&gt;

&lt;p&gt;That sounds impressive—until you watch the result.&lt;/p&gt;

&lt;p&gt;The shots may be technically correct, but the edit often has no hierarchy. The pacing feels templated. Color jumps between cameras. Subject edges flicker. A product reveal passes too quickly. The editor preview looks right, but the exported file does not.&lt;/p&gt;

&lt;p&gt;The problem is not always the model.&lt;/p&gt;

&lt;p&gt;In many systems, the agent has learned how to &lt;strong&gt;operate editing controls&lt;/strong&gt;, but not how to &lt;strong&gt;reason like an editor, colorist, or compositor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We are building &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;, an open-source AI video editor focused on editable projects, local-first media workflows, and verifiable output.&lt;/p&gt;

&lt;p&gt;In our latest update, we expanded the Timeline Studio video-editing skill with professional guidance for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source-time speed curves&lt;/li&gt;
&lt;li&gt;Color Wheels and shot matching&lt;/li&gt;
&lt;li&gt;person, product, and object cutouts&lt;/li&gt;
&lt;li&gt;subject-aware outlines&lt;/li&gt;
&lt;li&gt;effect ordering and export validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can inspect the exact update in &lt;a href="https://github.com/MartinDelophy/ai-video-editor/commit/df26d6460117c4c570b3cfb7ea320a3f91edca36" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The goal is simple: stop treating these capabilities as feature labels and start teaching the agent &lt;strong&gt;when to use them, why they matter, how to validate them, and when a result should be rejected&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap between an automatic edit and a professional edit
&lt;/h2&gt;

&lt;p&gt;Automatic editing usually answers questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which clips are relevant?&lt;/li&gt;
&lt;li&gt;Where are the silent sections?&lt;/li&gt;
&lt;li&gt;What captions should be generated?&lt;/li&gt;
&lt;li&gt;Where are the music beats?&lt;/li&gt;
&lt;li&gt;In what order should the shots appear?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Professional post-production adds a different layer of reasoning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does an action actually begin?&lt;/li&gt;
&lt;li&gt;Which frame contains the clearest consequence?&lt;/li&gt;
&lt;li&gt;Should the approach be accelerated while the result stays readable?&lt;/li&gt;
&lt;li&gt;Why do two adjacent shots feel visually disconnected?&lt;/li&gt;
&lt;li&gt;Is a detected bounding box good enough for compositing?&lt;/li&gt;
&lt;li&gt;Should the background be removed, or should the subject be emphasized while preserving context?&lt;/li&gt;
&lt;li&gt;Did the final export retain the timing, grade, matte, and edge treatment seen in the editor?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not minor details. They determine whether a video feels intentionally directed or mechanically assembled.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Speed curves should follow the action, not a template
&lt;/h2&gt;

&lt;p&gt;A common automated speed ramp looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1x -&amp;gt; 2x -&amp;gt; 0.5x -&amp;gt; 1x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It creates motion, but it does not necessarily create meaning.&lt;/p&gt;

&lt;p&gt;A useful speed curve begins with the source-time structure of the shot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;action onset
-&amp;gt; anticipation
-&amp;gt; approach
-&amp;gt; contact or reveal
-&amp;gt; readable result
-&amp;gt; release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after identifying those anchors should the agent change timing.&lt;/p&gt;

&lt;p&gt;For a product reveal, a motivated curve might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal speed
-&amp;gt; compress low-information travel
-&amp;gt; slow at the first unobstructed product frame
-&amp;gt; hold the hero result
-&amp;gt; return to normal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a software tutorial, the agent can accelerate cursor travel and waiting time, but it should return to normal speed before the click, state change, number, or result the viewer must understand.&lt;/p&gt;

&lt;p&gt;The skill also defines rejection conditions. A speed curve should fail review if it causes unreadable action, damaged dialogue, repeated boundary frames, visible stutter, broken audio continuity, or a weaker payoff than the original shot.&lt;/p&gt;

&lt;p&gt;The key principle is to preserve source-time reasoning even after the clip is retimed.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Color matching comes before a “cinematic” look
&lt;/h2&gt;

&lt;p&gt;Applying the same LUT or filter to every clip does not make a sequence consistent.&lt;/p&gt;

&lt;p&gt;Two shots can share the same preset and still disagree in white balance, exposure, black level, highlight color, skin tone, product color, saturation, and contrast.&lt;/p&gt;

&lt;p&gt;The updated skill uses a disciplined Color Wheels workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect representative native frames.&lt;/li&gt;
&lt;li&gt;Correct temperature and tint.&lt;/li&gt;
&lt;li&gt;Establish global exposure and luminance.&lt;/li&gt;
&lt;li&gt;Shape shadows, midtones, and highlights independently.&lt;/li&gt;
&lt;li&gt;Adjust saturation last.&lt;/li&gt;
&lt;li&gt;Compare adjacent shots at the actual cut boundary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Timeline Studio exposes controls for Shadows, Midtones, Highlights, Global Offset, Temperature, Tint, and Saturation.&lt;/p&gt;

&lt;p&gt;For portraits, the agent must preserve plausible skin, lips, teeth, eyes, and hair.&lt;/p&gt;

&lt;p&gt;For product shots, it must protect packaging, logos, materials, and brand colors. The background can move toward a complementary palette; the product itself should not be recolored merely to create a dramatic grade.&lt;/p&gt;

&lt;p&gt;Color parameters can be keyframed, but static correction remains the default. Animated grading should only be used when lighting or narrative intent genuinely changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Detection is not the same as a usable cutout
&lt;/h2&gt;

&lt;p&gt;Modern vision models can detect a person or product quickly. But a bounding box is only a region proposal.&lt;/p&gt;

&lt;p&gt;A production-ready cutout requires a stable alpha matte.&lt;/p&gt;

&lt;p&gt;For people, the agent needs to inspect hair, fingers, gaps between limbs, semi-transparent fabric, motion blur, held objects, occlusion, and re-entry.&lt;/p&gt;

&lt;p&gt;For products and objects, it needs to preserve handles, straps, holes, thin structures, transparent packaging, reflective edges, labels, logos, and the correct instance when similar objects appear.&lt;/p&gt;

&lt;p&gt;The workflow separates three stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;detect the target
-&amp;gt; generate the silhouette matte
-&amp;gt; validate temporal stability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For video, validation cannot stop at the first frame. The matte should be checked at fast-motion intervals, occlusions, shot boundaries, exits, re-entries, and the final frame.&lt;/p&gt;

&lt;p&gt;The result should be rejected if it shows target switching, missing body or product parts, edge chatter, stale masks, lag, or simplified geometry that damages the subject.&lt;/p&gt;

&lt;p&gt;Only after the raw matte passes review should the agent add a new background, depth, shadow, glow, or outline.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Outlines can preserve context while directing attention
&lt;/h2&gt;

&lt;p&gt;Removing the background is not always the right choice.&lt;/p&gt;

&lt;p&gt;In tutorials, interviews, product demonstrations, and documentary footage, the environment may contain information the viewer needs. A restrained subject outline can improve attention without destroying context.&lt;/p&gt;

&lt;p&gt;A simple routing rule works well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Need a new environment or foreground/background layering?
Use a cutout.

Need to preserve the environment but improve subject acquisition?
Use an outline.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful outline scenarios include presenter emphasis, product callouts, freeze-frame introductions, before/after comparisons, transitions between live footage and graphics, and small-screen attention guidance.&lt;/p&gt;

&lt;p&gt;The outline must be derived from a verified alpha matte—not from a detection rectangle.&lt;/p&gt;

&lt;p&gt;Its width, opacity, softness, glow, and shadow should be judged at the actual delivery size. An outline that looks subtle in a zoomed desktop preview may become overpowering on a phone, or disappear completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Professionalism comes from effect orchestration
&lt;/h2&gt;

&lt;p&gt;Adding more effects does not automatically create a better video.&lt;/p&gt;

&lt;p&gt;An agent with access to cutouts, outlines, depth, parallax, glow, Color Wheels, and speed ramps can easily over-process a shot unless every layer has an editorial job.&lt;/p&gt;

&lt;p&gt;A product hero shot might follow this order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;technical color match
-&amp;gt; product matte validation
-&amp;gt; background composition
-&amp;gt; restrained shadow or outline
-&amp;gt; motivated speed curve around the reveal
-&amp;gt; readable result hold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A presenter shot may need much less:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;person cutout
-&amp;gt; supporting graphics behind the presenter
-&amp;gt; subtle outline only where contrast requires it
-&amp;gt; normal speed during speech
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before adding an effect, the agent should be able to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it separate the subject?&lt;/li&gt;
&lt;li&gt;Does it clarify evidence?&lt;/li&gt;
&lt;li&gt;Does it improve rhythm?&lt;/li&gt;
&lt;li&gt;Does it support a narrative transition?&lt;/li&gt;
&lt;li&gt;Does it preserve the decisive frame?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the effect has no clear function, it should not be added.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The exported video—not the editor state—is the deliverable
&lt;/h2&gt;

&lt;p&gt;A correct editor preview does not guarantee a correct export.&lt;/p&gt;

&lt;p&gt;Complex projects can lose or alter grading state, alpha masks, edge treatment, time mapping, keyframe interpolation, or effect ordering.&lt;/p&gt;

&lt;p&gt;The delivery workflow is therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;preserve the editable project
-&amp;gt; preview the complete timeline
-&amp;gt; export the video
-&amp;gt; decode the exported file
-&amp;gt; compare matching timestamps
-&amp;gt; verify color, alpha, edges, and timing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent should retain the editable &lt;code&gt;.timeline&lt;/code&gt; project and verify the actual media file instead of trusting a success notification.&lt;/p&gt;

&lt;p&gt;There is also an important implementation boundary: advanced Color Wheels, speed curves, vision-derived masks, and subject effects currently belong to the editor/browser execution path. The headless renderer does not yet provide full parity for these features.&lt;/p&gt;

&lt;p&gt;We document that limitation instead of pretending every rendering path behaves identically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why encode this knowledge in a skill?
&lt;/h2&gt;

&lt;p&gt;A capable model can understand video concepts, but repeatable production work still benefits from explicit operational guidance.&lt;/p&gt;

&lt;p&gt;The skill provides routing rules, execution order, source-time reasoning, validation criteria, rejection conditions, current capability boundaries, and editable-project requirements.&lt;/p&gt;

&lt;p&gt;This turns “the editor supports Color Wheels” into something much more useful:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Match adjacent shots, protect identity-bearing colors, compare the real cut boundary, reject clipped or unstable results, and verify the export.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same principle applies to speed curves, cutouts, and outlines.&lt;/p&gt;

&lt;p&gt;Features become professional capabilities only when the agent knows how to reason about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;Automatic trimming, captions, music matching, and beat cuts are rapidly becoming baseline features.&lt;/p&gt;

&lt;p&gt;The next generation of AI video tools will compete on deeper questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the agent understand the visual consequence of an action?&lt;/li&gt;
&lt;li&gt;Can it build a real hierarchy of setup, rise, peak, and release?&lt;/li&gt;
&lt;li&gt;Can it preserve skin, products, and brand identity during grading?&lt;/li&gt;
&lt;li&gt;Can it produce temporally stable subject masks?&lt;/li&gt;
&lt;li&gt;Can it choose the smallest justified visual treatment?&lt;/li&gt;
&lt;li&gt;Can it validate the final render?&lt;/li&gt;
&lt;li&gt;Can it leave behind a project a human editor can continue modifying?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the direction we are exploring with Timeline Studio.&lt;/p&gt;

&lt;p&gt;If you are working on AI agents, browser-local ML, automated editing, media pipelines, or editable video formats, take a look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor/commit/df26d6460117c4c570b3cfb7ea320a3f91edca36" rel="noopener noreferrer"&gt;The professional post-production skill update&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback, issues, and stars are welcome.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Connected Codex for Chrome to an Open-Source Video Editing Skill—Here’s What Changed</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Mon, 17 Aug 2026 10:06:20 +0000</pubDate>
      <link>https://dev.to/martindelophy/i-connected-codex-for-chrome-to-an-open-source-video-editing-skill-heres-what-changed-38ac</link>
      <guid>https://dev.to/martindelophy/i-connected-codex-for-chrome-to-an-open-source-video-editing-skill-heres-what-changed-38ac</guid>
      <description>&lt;p&gt;AI can already write scripts, generate images, synthesize voices, and summarize videos. Yet real video production still feels fragmented.&lt;/p&gt;

&lt;p&gt;You watch a reference on YouTube. You open a product website in another tab. You copy timestamps into notes, download authorized assets, write narration, generate voice clips, and then rebuild all of that context inside an editor.&lt;/p&gt;

&lt;p&gt;Every step may contain AI, but the steps do not share a working memory.&lt;/p&gt;

&lt;p&gt;That is why the new &lt;a href="https://learn.chatgpt.com/docs/chrome-extension" rel="noopener noreferrer"&gt;Codex Chrome extension&lt;/a&gt; caught my attention. When combined with an editing-specific agent skill and an editor built around portable timelines, it creates something more useful than another “generate video” button:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A browser-aware agent that can turn researched context into an editable video project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have been exploring this workflow with &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;, an open-source, local-first browser video editor, and its &lt;code&gt;edit-timeline-studio&lt;/code&gt; agent skill.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8yvsav37hv955vfd8dj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8yvsav37hv955vfd8dj.png" alt="Timeline Studio multi-track editor" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The missing link was not generation
&lt;/h2&gt;

&lt;p&gt;Most AI video conversations focus on generation quality: longer clips, better motion, more consistent characters, or faster inference.&lt;/p&gt;

&lt;p&gt;Those improvements matter, but editing is a different problem.&lt;/p&gt;

&lt;p&gt;A real edit contains hundreds of decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which source moments should survive?&lt;/li&gt;
&lt;li&gt;Where should a spoken phrase end?&lt;/li&gt;
&lt;li&gt;Is a repeated shot building anticipation or just wasting time?&lt;/li&gt;
&lt;li&gt;Does the subject stay readable after reframing?&lt;/li&gt;
&lt;li&gt;Is every visible caption backed by audible speech?&lt;/li&gt;
&lt;li&gt;Should picture timing follow the narration, or should narration be rushed to fit a predetermined cut?&lt;/li&gt;
&lt;li&gt;Can a human revise the result without starting again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An agent that only clicks an editor is a faster mouse. An agent that only understands a webpage is a better researcher. The interesting system appears when browser context, editorial reasoning, and a real project model are connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Codex for Chrome changes
&lt;/h2&gt;

&lt;p&gt;OpenAI introduced Codex for Chrome on May 7, 2026. Later browser updates added several capabilities that are unusually relevant to content workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;referencing open Chrome tabs;&lt;/li&gt;
&lt;li&gt;bringing highlighted page text into a conversation;&lt;/li&gt;
&lt;li&gt;opening Ask ChatGPT from a page's context menu;&lt;/li&gt;
&lt;li&gt;using timestamped transcripts when captions are available on YouTube;&lt;/li&gt;
&lt;li&gt;working with sites where the user is already signed in;&lt;/li&gt;
&lt;li&gt;inspecting DOM, styles, console output, network traffic, and performance through Developer Mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important change is not simply that an agent can open a URL. It can work with the browser environment in which research is already happening.&lt;/p&gt;

&lt;p&gt;Open a reference video and the transcript can become structured evidence. Open a product website and the agent can verify which capabilities are actually visible. Open an authorized asset source and the agent can organize candidates without repeatedly asking the user to copy page context into chat.&lt;/p&gt;

&lt;p&gt;Browser understanding is the perception layer. It still needs an editing layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  An editing skill, not a mega-prompt
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt; is a local-first AI video editor that runs in the browser. It combines a multi-track timeline with captions, voiceovers, music, visual effects, transitions, and offline export.&lt;/p&gt;

&lt;p&gt;The repository also ships an agent skill named &lt;code&gt;edit-timeline-studio&lt;/code&gt;. The skill is not a single prompt that says “make this cinematic.” It defines how an agent should inspect, plan, execute, and verify an edit.&lt;/p&gt;

&lt;p&gt;Among other things, it requires an agent to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect duration, dimensions, media type, and audio before editing;&lt;/li&gt;
&lt;li&gt;analyze representative frames, speech, OCR, motion, and subject regions;&lt;/li&gt;
&lt;li&gt;classify the job as a talking-head cleanup, tutorial, highlight reel, multi-speaker edit, promotion, or reference reconstruction;&lt;/li&gt;
&lt;li&gt;record keep, remove, shorten, and reorder decisions against source time;&lt;/li&gt;
&lt;li&gt;build narration from short physical voice assets and lock the audio spine before timing the picture;&lt;/li&gt;
&lt;li&gt;verify captions against audible speech;&lt;/li&gt;
&lt;li&gt;inspect boundaries, transitions, audio behavior, and the final decoded render;&lt;/li&gt;
&lt;li&gt;deliver both a rendered video and a reopenable &lt;code&gt;.timeline&lt;/code&gt; project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The editable project remains the source of truth. The render is an output, not the only artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  A three-layer video agent
&lt;/h2&gt;

&lt;p&gt;The combined architecture is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Codex Chrome extension
  authorized tabs, signed-in pages, selected text, timestamped transcripts
                              ↓
edit-timeline-studio skill
  evidence analysis, narrative planning, edit decisions, execution, validation
                              ↓
Timeline Studio
  tracks, clips, captions, voiceovers, music, effects, .timeline project, render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a distinct responsibility.&lt;/p&gt;

&lt;p&gt;Chrome answers: &lt;strong&gt;What is happening in the user's web context?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The skill answers: &lt;strong&gt;What editorial decisions should be made, and how should they be verified?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Timeline Studio answers: &lt;strong&gt;Where do those decisions live so that a human can continue editing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This separation matters. It prevents browser automation from being mistaken for editorial judgment, and it prevents AI output from collapsing into an opaque one-shot render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 1: reconstruct why a reference video works
&lt;/h2&gt;

&lt;p&gt;Asking an AI to “summarize this video” rarely produces an actionable edit plan. A serious reference reconstruction needs to identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shot and sub-shot boundaries;&lt;/li&gt;
&lt;li&gt;repeated source families;&lt;/li&gt;
&lt;li&gt;speed ramps, holds, and reversals;&lt;/li&gt;
&lt;li&gt;transition anatomy;&lt;/li&gt;
&lt;li&gt;subject center, scale, and movement path;&lt;/li&gt;
&lt;li&gt;changes in look or filter state;&lt;/li&gt;
&lt;li&gt;the relationship between retained audio, narration, captions, and picture;&lt;/li&gt;
&lt;li&gt;setup, rise, pre-impact, peak, aftershock, and bridge beats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the reference open in Chrome, the agent can use the available transcript and page context as one evidence lane. Local analysis can then inspect the actual media for timing, motion, composition, and audio.&lt;/p&gt;

&lt;p&gt;A task can begin like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Chrome analyze the reference video in my current tab and use its timestamped
transcript when available. Identify the narrative sections and verified page
context.

Then use the edit-timeline-studio skill to reconstruct the shot order,
repetitions, speed changes, transitions, subject movement, and tension curve.
Use only media I provide or media with an explicit platform-provided download
and suitable reuse rights. Deliver an editable .timeline project and a verified
render. Mark anything that cannot be established from evidence.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is no longer just an explanation of the video. It is an explanation expressed as an editable timeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 2: turn a product website into a narrative, not a feature reel
&lt;/h2&gt;

&lt;p&gt;Many AI-generated product videos are feature lists with animated typography. Motion is present, but no transformation occurs.&lt;/p&gt;

&lt;p&gt;A better promotional structure gives each example a complete loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context → friction → product action → visible result → consequence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Chrome extension can help an agent inspect an authorized product journey across the homepage, feature pages, documentation, and the actual interface. The agent can build a coverage manifest and distinguish verified behavior from marketing claims.&lt;/p&gt;

&lt;p&gt;The editing skill can then organize multiple visually distinct examples into a larger arc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;problem → transformation → proof → payoff → call to action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Narration is created as short breath-group assets first. Captions and picture timing are derived from the accepted speech sequence, rather than globally speeding up a voice to hit an arbitrary runtime.&lt;/p&gt;

&lt;p&gt;This is especially useful for open-source projects. A README tells people what a project contains. A narrative video can show which part of someone's workflow actually changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 3: close the loop between editor development and visual QA
&lt;/h2&gt;

&lt;p&gt;The Chrome integration is also useful while building Timeline Studio itself.&lt;/p&gt;

&lt;p&gt;With Developer Mode, Codex can inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOM and applied styles after timeline interactions;&lt;/li&gt;
&lt;li&gt;WebGPU or ONNX Runtime initialization failures;&lt;/li&gt;
&lt;li&gt;worker messages, model requests, and caching behavior;&lt;/li&gt;
&lt;li&gt;runtime errors in the console;&lt;/li&gt;
&lt;li&gt;responsive inspector layouts;&lt;/li&gt;
&lt;li&gt;performance problems during preview or export.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, Codex can work with the local repository and terminal. That creates a tight loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;observe the rendered problem
→ inspect runtime evidence
→ locate the implementation
→ make a scoped change
→ reopen and verify the UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a browser application that combines a multi-track timeline, media decoding, WebGPU inference, workers, and offline rendering, that shared loop is much more useful than debugging from a screenshot alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two execution paths instead of pretending every click is deterministic
&lt;/h2&gt;

&lt;p&gt;Timeline Studio uses two automation paths.&lt;/p&gt;

&lt;p&gt;The versioned command runner directly inspects and modifies portable &lt;code&gt;.timeline&lt;/code&gt; archives. It supports semantic diffs and transactional operations for the commands it registers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.inspect /projects/demo.timeline
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.diff /projects/edit-plan.json
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.run /projects/edit-plan.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser path handles AI generation, visual preview, rich rendering, effects, and operations that are not yet available through the command contract.&lt;/p&gt;

&lt;p&gt;The distinction is deliberate: deterministic project operations belong in the command layer; visual or UI-only operations belong in the browser layer and require visible verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the editable project matters
&lt;/h2&gt;

&lt;p&gt;If an AI video tool returns only an MP4, every correction becomes a regeneration problem.&lt;/p&gt;

&lt;p&gt;Timeline Studio treats &lt;code&gt;.timeline&lt;/code&gt; as a portable project archive containing project structure and media. After the agent finishes, a human can still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adjust a cut;&lt;/li&gt;
&lt;li&gt;replace one voice segment;&lt;/li&gt;
&lt;li&gt;move a caption;&lt;/li&gt;
&lt;li&gt;change music;&lt;/li&gt;
&lt;li&gt;revise a transition;&lt;/li&gt;
&lt;li&gt;replace media and export again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI should remove repetitive setup work without removing authorship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Powerful browser access needs narrow boundaries
&lt;/h2&gt;

&lt;p&gt;Browser-aware agents also create real security and rights-management questions. A practical workflow should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;grant site access per task or domain rather than allowing every site by default;&lt;/li&gt;
&lt;li&gt;enter credentials only in the browser, never in chat;&lt;/li&gt;
&lt;li&gt;treat page content as untrusted input;&lt;/li&gt;
&lt;li&gt;use web footage only through publisher- or platform-provided downloads with suitable item-level rights;&lt;/li&gt;
&lt;li&gt;preserve watermarks and provenance;&lt;/li&gt;
&lt;li&gt;require human confirmation for publishing, paid generation, permission changes, or deletion;&lt;/li&gt;
&lt;li&gt;keep media processing local when the capability supports it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The browser is a powerful perception and action surface. It should not become an invisible permission bypass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live editor:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;video-editor.ai-creator.top&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill directory:&lt;/strong&gt; &lt;a href="https://skills.sh/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;skills.sh/MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the skill through skills.sh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add MartinDelophy/ai-video-editor &lt;span class="nt"&gt;--skill&lt;/span&gt; edit-timeline-studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install it for Codex with GitHub CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh skill &lt;span class="nb"&gt;install &lt;/span&gt;MartinDelophy/ai-video-editor edit-timeline-studio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--agent&lt;/span&gt; codex &lt;span class="nt"&gt;--scope&lt;/span&gt; user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The next step is not a bigger Generate button
&lt;/h2&gt;

&lt;p&gt;The next generation of AI editing will not be defined only by faster models or longer generated clips.&lt;/p&gt;

&lt;p&gt;It will also depend on whether an agent can enter the creator's real information environment, understand why material matters, make explainable editorial decisions, validate the result, and leave those decisions on a timeline that a human still controls.&lt;/p&gt;

&lt;p&gt;Codex for Chrome gives the agent browser context. The Timeline Studio skill provides an editing discipline. Timeline Studio turns those decisions into a portable project.&lt;/p&gt;

&lt;p&gt;That combination is the interesting part: not AI replacing the timeline, but AI finally learning how to work inside one.&lt;/p&gt;

&lt;p&gt;If you are interested in browser media, WebCodecs, WebGPU, ONNX Runtime, agent skills, or editable AI workflows, I would love your feedback and contributions on &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.chatgpt.com/docs/chrome-extension" rel="noopener noreferrer"&gt;OpenAI: Chrome extension documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.chatgpt.com/docs/changelog" rel="noopener noreferrer"&gt;OpenAI: ChatGPT &amp;amp; Codex changelog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building a Safe Video-Editing Agent for DeepSeek Harness</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Sat, 15 Aug 2026 09:34:03 +0000</pubDate>
      <link>https://dev.to/martindelophy/building-a-safe-video-editing-agent-for-deepseek-harness-2n1a</link>
      <guid>https://dev.to/martindelophy/building-a-safe-video-editing-agent-for-deepseek-harness-2n1a</guid>
      <description>&lt;p&gt;Large language models can explain how a video &lt;em&gt;should&lt;/em&gt; be edited. But safely editing a real project is a different problem.&lt;/p&gt;

&lt;p&gt;If you ask an agent to “convert this project to 9:16, keep the captions, save a copy, and render an MP4,” the hard part is not understanding the sentence. The hard part is making sure the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;selects the correct project, track, and clips;&lt;/li&gt;
&lt;li&gt;does not overwrite the original;&lt;/li&gt;
&lt;li&gt;does not execute the same operation twice after a retry;&lt;/li&gt;
&lt;li&gt;does not write from a stale project revision;&lt;/li&gt;
&lt;li&gt;cannot read or write outside the approved workspace;&lt;/li&gt;
&lt;li&gt;and verifies that the rendered video actually exists and is valid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To explore this problem, I built and open-sourced &lt;strong&gt;dsh-timeline-studio-plugin&lt;/strong&gt;, a community plugin that connects DeepSeek Harness to Timeline Studio’s deterministic &lt;code&gt;.timeline&lt;/code&gt; command layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugin: &lt;a href="https://github.com/MartinDelophy/dsh-timeline-studio-plugin" rel="noopener noreferrer"&gt;MartinDelophy/dsh-timeline-studio-plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Editor: &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a community integration for Timeline Studio. It is not part of DeepSeek Harness core and is not an official DeepSeek project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FMartinDelophy%2Fdsh-timeline-studio-plugin%2Fmain%2Fdocs%2Fimages%2Fplayer-flow.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FMartinDelophy%2Fdsh-timeline-studio-plugin%2Fmain%2Fdocs%2Fimages%2Fplayer-flow.svg" alt="From a natural-language request to an editable Timeline Studio project" width="1600" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the plugin does
&lt;/h2&gt;

&lt;p&gt;The plugin is not another video editor UI. It is an automation bridge between an agent and the editor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeline Studio&lt;/strong&gt; remains responsible for the visual timeline, media processing, browser-local AI features, preview, and the final creative experience. &lt;strong&gt;dsh-timeline-studio-plugin&lt;/strong&gt; exposes a small, deterministic tool surface that DeepSeek Harness can call.&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Natural-language request
  ↓
DeepSeek Harness
  ↓
Read-only project inspection
  ↓
Structured edit plan
  ↓
Semantic diff
  ↓
Transactional apply to a new .timeline project
  ↓
Render and validate MP4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model decides &lt;em&gt;what should happen&lt;/em&gt;. The command layer decides whether that operation is allowed and valid for the current project state.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the user sees
&lt;/h2&gt;

&lt;p&gt;The user first adds a local workspace containing a &lt;code&gt;.timeline&lt;/code&gt; project and its media assets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70e9425mk7i8zf56go6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70e9425mk7i8zf56go6u.png" alt="DeepSeek Harness workspace screen" width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A safe first prompt is deliberately read-only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Inspect the Timeline Studio project in this workspace. Tell me its duration, aspect ratio, tracks, and media. Do not modify any files yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After reviewing the result, the user can continue with an editing request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change the project to 9:16. Show me the planned diff first. If it is valid, save it as a new project without overwriting the original, then render an MP4.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The plugin has no separate visual panel. Its work appears in Harness tool calls and in the resulting &lt;code&gt;.timeline&lt;/code&gt; and MP4 files. Rich visual inspection and manual refinement still happen in Timeline Studio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven tools, three responsibilities
&lt;/h2&gt;

&lt;p&gt;The first version exposes seven model tools:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_project_inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect revision, duration, ratio, tracks, media inventory, and warnings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_track_inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List clips on a track in timeline order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_clip_inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect source mapping, timing, transforms, and relationships&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_transcript_inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect captions, word timing, speakers, and audio links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_project_diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate an edit plan against the real command registry without writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_project_apply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a validated plan transactionally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeline_studio_project_render&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render and validate an H.264/AAC MP4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FMartinDelophy%2Fdsh-timeline-studio-plugin%2Fmain%2Fdocs%2Fimages%2Fcapabilities.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FMartinDelophy%2Fdsh-timeline-studio-plugin%2Fmain%2Fdocs%2Fimages%2Fcapabilities.svg" alt="Three capability groups: understand, edit safely, and deliver" width="1600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the agent does not rewrite project JSON directly
&lt;/h2&gt;

&lt;p&gt;The shortest implementation would be to send the entire project JSON to the model and ask it to return a modified copy.&lt;/p&gt;

&lt;p&gt;That is also the least reliable approach.&lt;/p&gt;

&lt;p&gt;Syntactically valid JSON can still be semantically invalid for the editor. The model might reference a deleted clip, place media on the wrong track, invent an unsupported property, or repeat an operation that was already committed.&lt;/p&gt;

&lt;p&gt;Instead, the agent produces a structured edit plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/projects/input.timeline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"baseRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dryRun"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"set-ratio-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project.set_ratio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ratio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9:16"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/projects/output.timeline"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan is checked against Timeline Studio’s real command registry before it is allowed to write anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diff before apply
&lt;/h2&gt;

&lt;p&gt;Every edit goes through two distinct stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timeline_studio_project_diff
  ↓ only after a successful diff
timeline_studio_project_apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diff stage does not write the output project. It checks questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the command exist?&lt;/li&gt;
&lt;li&gt;Are the arguments valid?&lt;/li&gt;
&lt;li&gt;Does the target clip still exist?&lt;/li&gt;
&lt;li&gt;Does &lt;code&gt;baseRevision&lt;/code&gt; match the current project?&lt;/li&gt;
&lt;li&gt;Has the operation ID already been used?&lt;/li&gt;
&lt;li&gt;Do all input and output paths stay inside the approved roots?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A wrong chat response can be regenerated. A wrong local write can damage a user’s project. That is why the preview step is a required safety gate rather than an optional UX enhancement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making retries safe with revisions and operation IDs
&lt;/h2&gt;

&lt;p&gt;Agent tool calls can be retried because of cancellations, process failures, network interruptions, or replanning.&lt;/p&gt;

&lt;p&gt;The plugin uses two mechanisms to make those retries predictable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project revision
&lt;/h3&gt;

&lt;p&gt;Inspection returns the current revision. The edit plan must include it as &lt;code&gt;baseRevision&lt;/code&gt;. If the project has changed since inspection, a new write based on the stale revision is rejected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotent operation ID
&lt;/h3&gt;

&lt;p&gt;Every operation has a stable ID. Resubmitting an ID that has already been applied becomes a no-op instead of adding the same clip or modification twice.&lt;/p&gt;

&lt;p&gt;Together, these checks provide optimistic concurrency control and idempotent execution for agent-driven edits.&lt;/p&gt;

&lt;h2&gt;
  
  
  File access boundaries belong in code, not prompts
&lt;/h2&gt;

&lt;p&gt;Video editing requires access to project files, source media, and render outputs. A system prompt saying “do not leave the workspace” is not a security boundary.&lt;/p&gt;

&lt;p&gt;The plugin requires explicit &lt;code&gt;allowedRoots&lt;/code&gt; configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dsh-timeline-studio-plugin'&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;timelineStudioRoot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/absolute/path/web_player&lt;/span&gt;
    &lt;span class="na"&gt;allowedRoots&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/absolute/path/projects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Projects, plans, imported media, and output files must all resolve inside those roots. The implementation also blocks symbolic-link escapes.&lt;/p&gt;

&lt;p&gt;This is a general rule I find useful for agent tooling: &lt;strong&gt;if a restriction can be enforced deterministically, enforce it in code instead of asking the model to remember it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;The currently verified environment is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek Harness &lt;code&gt;0.1.0-rc.6&lt;/code&gt; Developer Preview&lt;/li&gt;
&lt;li&gt;Node.js &lt;code&gt;22.20+&lt;/code&gt; or &lt;code&gt;24+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a local Timeline Studio repository with dependencies installed&lt;/li&gt;
&lt;li&gt;FFmpeg and ffprobe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the GitHub repository as a DSH Web-profile bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"github:MartinDelophy/dsh-timeline-studio-plugin#main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start Harness with absolute paths for Timeline Studio and the project workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TIMELINE_STUDIO_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/absolute/path/web_player &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TIMELINE_PROJECTS_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/absolute/path/projects &lt;span class="se"&gt;\&lt;/span&gt;
dsh &lt;span class="nt"&gt;--profile&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bundle stays disabled when &lt;code&gt;TIMELINE_STUDIO_ROOT&lt;/code&gt; is missing, so an incomplete installation does not break an existing Harness profile.&lt;/p&gt;

&lt;p&gt;After restarting, open &lt;strong&gt;Settings → Plugins → Plugin list&lt;/strong&gt; and search for &lt;code&gt;timeline&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dd5s1knn5y6v93mjj19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dd5s1knn5y6v93mjj19.png" alt="Timeline Studio plugin enabled and mounted in DeepSeek Harness" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the configuration is enabled and the Cordis status is mounted, the plugin is ready. Users do not call the mount identifier manually; they simply describe the editing task in a Harness conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was tested end to end
&lt;/h2&gt;

&lt;p&gt;Registering tools is not enough. The project also exercises the real DeepSeek Harness and Cordis pipeline.&lt;/p&gt;

&lt;p&gt;The verified path covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DSH bundle installation;&lt;/li&gt;
&lt;li&gt;automatic Cordis mounting;&lt;/li&gt;
&lt;li&gt;registration of all seven tools;&lt;/li&gt;
&lt;li&gt;inspection of a real &lt;code&gt;.timeline&lt;/code&gt; project;&lt;/li&gt;
&lt;li&gt;confirmation that diff performs no project write;&lt;/li&gt;
&lt;li&gt;revision and idempotency checks during apply;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allowedRoots&lt;/code&gt; and symbolic-link boundary enforcement;&lt;/li&gt;
&lt;li&gt;cancellation propagation to the Timeline Studio subprocess;&lt;/li&gt;
&lt;li&gt;reinspection of the generated project;&lt;/li&gt;
&lt;li&gt;MP4 rendering and output validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The local checks are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run check
&lt;span class="nv"&gt;TIMELINE_STUDIO_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/absolute/path/web_player npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek Harness is still a Developer Preview, so breaking changes are possible. The plugin intentionally keeps the Harness adapter thin and leaves editing behavior in Timeline Studio. If the host interface changes, the integration layer can evolve without rewriting the editor’s command engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the editable project matters
&lt;/h2&gt;

&lt;p&gt;Many AI video systems return only a final render. That works for one-shot generation, but it makes small follow-up changes expensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extend a shot by half a second;&lt;/li&gt;
&lt;li&gt;correct one caption;&lt;/li&gt;
&lt;li&gt;lower the music;&lt;/li&gt;
&lt;li&gt;create a vertical variant;&lt;/li&gt;
&lt;li&gt;or reuse the same project for another campaign.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This workflow returns both the video and an editable &lt;code&gt;.timeline&lt;/code&gt; project. The agent can handle repetitive, structured, verifiable work, while the creator keeps control over the final cut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The interesting part of an agent that edits video is not just whether the model understands editing vocabulary. The real engineering questions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What can the agent access?&lt;/li&gt;
&lt;li&gt;Which project revision is it acting on?&lt;/li&gt;
&lt;li&gt;Was the operation already executed?&lt;/li&gt;
&lt;li&gt;Can the edit be previewed before writing?&lt;/li&gt;
&lt;li&gt;Can the result be validated afterward?&lt;/li&gt;
&lt;li&gt;Can a human continue editing the result?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dsh-timeline-studio-plugin&lt;/code&gt; is an open-source attempt to answer those questions with a small deterministic tool layer around a real editor.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/dsh-timeline-studio-plugin" rel="noopener noreferrer"&gt;dsh-timeline-studio-plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues, feedback, and contributions are welcome.&lt;/p&gt;

</description>
      <category>aiai</category>
    </item>
    <item>
      <title>How Four Color Wheels Work in Video Editing—and How I Built Them in the Browser</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Wed, 12 Aug 2026 08:11:53 +0000</pubDate>
      <link>https://dev.to/martindelophy/how-four-color-wheels-work-in-video-editing-and-how-i-built-them-in-the-browser-2jfd</link>
      <guid>https://dev.to/martindelophy/how-four-color-wheels-work-in-video-editing-and-how-i-built-them-in-the-browser-2jfd</guid>
      <description>&lt;h2&gt;
  
  
  I built this in an open-source browser video editor
&lt;/h2&gt;

&lt;p&gt;I recently added a complete desktop color-wheels workflow to &lt;strong&gt;Timeline Studio&lt;/strong&gt;, an open-source, local-first video editor that runs in the browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;https://github.com/MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live editor:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top" rel="noopener noreferrer"&gt;https://video-editor.ai-creator.top&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate wheels for shadows, midtones, highlights, and global offset;&lt;/li&gt;
&lt;li&gt;temperature, tint, and global saturation controls;&lt;/li&gt;
&lt;li&gt;hue, saturation, and luminance controls for every wheel;&lt;/li&gt;
&lt;li&gt;independent keyframes for all 15 grading properties;&lt;/li&gt;
&lt;li&gt;shortest-path hue interpolation;&lt;/li&gt;
&lt;li&gt;the same animated grade in preview, transitions, and final export;&lt;/li&gt;
&lt;li&gt;a desktop-focused interface, while the mobile web editor keeps its simpler speed workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have ever opened a professional video editor and wondered why it displays four colorful circles, this article explains what they do. The second half looks at the engineering problems behind implementing them in a browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a color wheel?
&lt;/h2&gt;

&lt;p&gt;A color wheel is a two-dimensional controller for expressing both a color direction and an adjustment strength.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moving the control point toward red adds a red bias.&lt;/li&gt;
&lt;li&gt;Moving it toward blue adds a blue bias.&lt;/li&gt;
&lt;li&gt;Moving farther from the center increases the strength.&lt;/li&gt;
&lt;li&gt;Leaving it in the center adds no directional color bias.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is often more intuitive than controlling red, green, and blue with three unrelated sliders. If an image feels too cold, you can move toward orange. If it feels too warm, you can move toward blue.&lt;/p&gt;

&lt;p&gt;In a useful grading tool, however, the wheel does not simply add the same color to every pixel. A frame contains dark, medium, and bright regions, and they usually need different treatment. That is why professional editors provide multiple wheels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are there four wheels?
&lt;/h2&gt;

&lt;p&gt;One frame may contain almost-black hair, naturally exposed skin, a white shirt, and a bright window at the same time.&lt;/p&gt;

&lt;p&gt;With only one global color control, making the dark areas cooler would also make the skin and window cooler. Color-grading tools therefore divide their influence primarily by luminance range.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Wheel&lt;/th&gt;
&lt;th&gt;Main area of influence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shadows&lt;/td&gt;
&lt;td&gt;Dark clothing, hair, night backgrounds, and unlit areas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Midtones&lt;/td&gt;
&lt;td&gt;Skin, products, walls, and most normally exposed subjects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Highlights&lt;/td&gt;
&lt;td&gt;Windows, sky, lamps, reflections, and bright surfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offset&lt;/td&gt;
&lt;td&gt;The overall color balance of the frame&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Shadows
&lt;/h3&gt;

&lt;p&gt;Moving the shadows slightly toward blue or cyan can make a shot feel colder and deeper. This is one ingredient commonly associated with a cinematic look.&lt;/p&gt;

&lt;p&gt;It is also easy to overdo. Excessive adjustment can turn black areas visibly blue, damage natural hair color, and make low-light regions look dirty or posterized. Subtle adjustments tend to work better than large, obvious movements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Midtones
&lt;/h3&gt;

&lt;p&gt;Midtones often contain the most important part of the image: a person's skin or the main product.&lt;/p&gt;

&lt;p&gt;If a face looks pale, moving the midtones slightly toward a warm color may help. Pushing too far can make skin yellow or red and contaminate neutral clothing and backgrounds.&lt;/p&gt;

&lt;p&gt;A safer order of operations is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Correct exposure.&lt;/li&gt;
&lt;li&gt;Correct white balance.&lt;/li&gt;
&lt;li&gt;Check whether skin and neutral objects look natural.&lt;/li&gt;
&lt;li&gt;Use the midtone wheel for a small creative adjustment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A color wheel is excellent for refinement, but it should not hide a fundamentally incorrect exposure or white balance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Highlights
&lt;/h3&gt;

&lt;p&gt;Highlights affect bright areas such as sky, windows, lamps, reflections, white clothing, and the lit side of a face.&lt;/p&gt;

&lt;p&gt;Moving highlights toward yellow or orange can strengthen the feeling of sunlight or sunset. Moving them toward blue can create a colder night or technology-oriented look.&lt;/p&gt;

&lt;p&gt;A frequent creative choice is to keep shadows slightly cool and highlights slightly warm. This creates color separation and additional depth. It does not mean that every video should use an aggressive teal-and-orange preset. Interviews, food, products, and natural scenes all have different requirements for color accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offset
&lt;/h3&gt;

&lt;p&gt;Offset affects the whole frame rather than a single luminance range.&lt;/p&gt;

&lt;p&gt;It is useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the entire clip has a green cast;&lt;/li&gt;
&lt;li&gt;a shot is globally too cold or too warm;&lt;/li&gt;
&lt;li&gt;several cameras need a common starting balance;&lt;/li&gt;
&lt;li&gt;you want to establish a gentle global direction before regional adjustments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If only the shadows are green while the highlights are correct, a global offset is the wrong tool. The important question is not only &lt;em&gt;which color should change?&lt;/em&gt; but also &lt;em&gt;which luminance range should change?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the curved control beside the wheel?
&lt;/h2&gt;

&lt;p&gt;Many color-wheel interfaces place a curved control next to the circle. It usually controls luminance for that range.&lt;/p&gt;

&lt;p&gt;A complete wheel therefore represents three important values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hue:&lt;/strong&gt; which color direction to use;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saturation:&lt;/strong&gt; how strong that color direction should be;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Luminance:&lt;/strong&gt; whether the affected range should become brighter or darker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The disc handles hue and saturation, while the curved control changes luminance. Together they provide much more useful control than a decorative color picker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Color wheels are not the same as filters
&lt;/h2&gt;

&lt;p&gt;A filter is usually a predefined group of adjustments. It is excellent for reaching a recognizable style quickly.&lt;/p&gt;

&lt;p&gt;A color wheel is a manual correction and grading tool.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter&lt;/th&gt;
&lt;th&gt;Color wheel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Produces a quick result&lt;/td&gt;
&lt;td&gt;Provides detailed control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hides many values in a preset&lt;/td&gt;
&lt;td&gt;Exposes luminance-specific decisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can impose the same style on every source&lt;/td&gt;
&lt;td&gt;Can preserve the character of each source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works well for rapid exploration&lt;/td&gt;
&lt;td&gt;Works well for correction and refinement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They can also work together. A user can apply a filter as a starting point and then repair skin, shadows, or highlights with the wheels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video color needs keyframes
&lt;/h2&gt;

&lt;p&gt;A still image can use one set of parameters. Video changes over time.&lt;/p&gt;

&lt;p&gt;A subject may walk from indoors to sunlight. Stage lighting may change from blue to red. A memory sequence may gradually lose saturation. A sunset shot may become warmer as it progresses.&lt;/p&gt;

&lt;p&gt;Keyframes allow a clip to store different grading states at different times:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0s&lt;/code&gt;: neutral color;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2s&lt;/code&gt;: midtones begin moving warmer;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;4s&lt;/code&gt;: highlights gain a small yellow bias;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;6s&lt;/code&gt;: global saturation decreases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The editor interpolates between these states. Color grading stops being a fixed filter and becomes an animation that follows the content.&lt;/p&gt;

&lt;p&gt;In Timeline Studio, the keyframeable properties are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;temperature, tint, and global saturation;&lt;/li&gt;
&lt;li&gt;hue, saturation, and luminance for shadows;&lt;/li&gt;
&lt;li&gt;hue, saturation, and luminance for midtones;&lt;/li&gt;
&lt;li&gt;hue, saturation, and luminance for highlights;&lt;/li&gt;
&lt;li&gt;hue, saturation, and luminance for offset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That produces 15 independently keyframeable properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ordinary interpolation breaks hue
&lt;/h2&gt;

&lt;p&gt;Hue is circular, not linear.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;350°&lt;/code&gt; and &lt;code&gt;10°&lt;/code&gt; are only 20 degrees apart on a color wheel. Ordinary numeric interpolation may travel from &lt;code&gt;350°&lt;/code&gt; through &lt;code&gt;180°&lt;/code&gt; to &lt;code&gt;10°&lt;/code&gt;, passing through many unrelated colors.&lt;/p&gt;

&lt;p&gt;The desired transition crosses zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;350° -&amp;gt; 0° -&amp;gt; 10°
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A shortest-path interpolation can be implemented like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;interpolateHue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentHue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextHue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nextHue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;currentHue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;540&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentHue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;progress&lt;/code&gt; ranges from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;. The normalized delta stays between &lt;code&gt;-180&lt;/code&gt; and &lt;code&gt;180&lt;/code&gt;, so the transition follows the shorter direction around the wheel.&lt;/p&gt;

&lt;p&gt;This is a small mathematical detail with a very visible result. Without it, animated color may unexpectedly cycle through green, cyan, or purple between two nearby red hues.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical data model
&lt;/h2&gt;

&lt;p&gt;A simplified base grade can be represented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;colorGrade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shadows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;midtones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;highlights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;luminance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each animated property can use a path such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;colorGrade.temperature
colorGrade.shadows.hue
colorGrade.highlights.luminance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At render time, the resolver needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find an exact keyframe at the current time if one exists.&lt;/li&gt;
&lt;li&gt;Otherwise find the nearest previous and next values for the property.&lt;/li&gt;
&lt;li&gt;Interpolate ordinary numeric properties linearly.&lt;/li&gt;
&lt;li&gt;Interpolate hue using the shortest circular path.&lt;/li&gt;
&lt;li&gt;Use the base grade before the first keyframe or when a property has no keyframes.&lt;/li&gt;
&lt;li&gt;Normalize and clamp the resolved values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Property-level keyframes are important. A user may want to animate highlight luminance without freezing every other color property into the same keyframe object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview and export must use the same resolver
&lt;/h2&gt;

&lt;p&gt;Drawing attractive wheels is only the UI portion of the feature. A video editor is not correct unless the final export matches the editor preview.&lt;/p&gt;

&lt;p&gt;If preview and export use separate color logic, users may encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;different colors after export;&lt;/li&gt;
&lt;li&gt;color jumps during transitions;&lt;/li&gt;
&lt;li&gt;keyframes that work in the editor but disappear from the final video;&lt;/li&gt;
&lt;li&gt;different results for images and video clips.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Timeline Studio resolves the grade from the clip's keyframes and local time, then sends that result through the preview, transition, and offline export paths.&lt;/p&gt;

&lt;p&gt;The next clip in a transition must resolve its own local grading state as well. Otherwise the outgoing clip may be correct while the incoming clip temporarily displays a static or incorrect grade.&lt;/p&gt;

&lt;p&gt;This shared resolver was one of the most important parts of the implementation. The wheels are not decorative UI; they are editable parameters that reach the final rendered artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter thumbnails should use the user's source
&lt;/h2&gt;

&lt;p&gt;Another small but important detail is filter preview imagery.&lt;/p&gt;

&lt;p&gt;A filter grid should not replace the user's image with an unrelated stock thumbnail. For an image clip, every filter card can reuse the selected image. For a video clip, the editor can reuse an extracted frame from the selected clip.&lt;/p&gt;

&lt;p&gt;Only the preview filter should change. The source image must remain the same across cards, so users can compare color treatments rather than compare different subjects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the full UI is desktop-only
&lt;/h2&gt;

&lt;p&gt;Four wheels, basic controls, value readouts, reset actions, and 15 keyframe buttons form a dense interface.&lt;/p&gt;

&lt;p&gt;Desktop users have enough space and a precise pointer. Copying the same layout directly to mobile web would create tiny wheels, accidental touches, an extremely long inspector, and competition with the timeline for screen space.&lt;/p&gt;

&lt;p&gt;For this release, Timeline Studio exposes the complete color-wheels workflow on desktop. Mobile web does not show the wheels and keeps the more focused video-speed workflow.&lt;/p&gt;

&lt;p&gt;Feature consistency does not always require identical UI on every device. The interface should reflect the precision and space available on each platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple grading order for beginners
&lt;/h2&gt;

&lt;p&gt;If you are new to color wheels, this order is a useful starting point:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Disable creative filters and inspect the original source.&lt;/li&gt;
&lt;li&gt;Correct temperature and tint.&lt;/li&gt;
&lt;li&gt;Check exposure and preserve useful shadow and highlight detail.&lt;/li&gt;
&lt;li&gt;Inspect midtones, especially skin or the main product.&lt;/li&gt;
&lt;li&gt;Add subtle separation to shadows and highlights.&lt;/li&gt;
&lt;li&gt;Use offset only when the whole frame needs a global correction.&lt;/li&gt;
&lt;li&gt;Toggle the grade on and off frequently because human vision adapts quickly.&lt;/li&gt;
&lt;li&gt;Add keyframes when lighting changes over time instead of forcing one static grade onto the whole clip.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The four wheels can be remembered simply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shadows:&lt;/strong&gt; dark regions;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Midtones:&lt;/strong&gt; subjects and skin;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highlights:&lt;/strong&gt; bright regions and the character of light;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offset:&lt;/strong&gt; the entire frame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good grading is rarely about pushing every control to a dramatic value. It usually comes from several small, intentional adjustments that preserve the identity of the footage while supporting its mood.&lt;/p&gt;

&lt;p&gt;The complete implementation is open source. You can inspect the UI, property keyframes, shortest-path hue interpolation, preview composition, transition handling, and export integration in the repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;https://github.com/MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live editor:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top" rel="noopener noreferrer"&gt;https://video-editor.ai-creator.top&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If browser-based video editing, React, WebCodecs, local AI, or deterministic media export interests you, issues and pull requests are welcome. If the project is useful, a GitHub star also helps more developers discover it.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Browser-Native AI Video Editor: Timeline Synchronization, WebGPU, and Worker Architecture</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Tue, 11 Aug 2026 08:41:24 +0000</pubDate>
      <link>https://dev.to/martindelophy/building-a-browser-native-ai-video-editor-timeline-synchronization-webgpu-and-worker-architecture-1el</link>
      <guid>https://dev.to/martindelophy/building-a-browser-native-ai-video-editor-timeline-synchronization-webgpu-and-worker-architecture-1el</guid>
      <description>&lt;p&gt;Modern browsers can now do work that once required a native desktop application. WebGPU exposes GPU compute, WebAssembly brings mature runtimes to the web, WebCodecs provides lower-level media primitives, and Web Workers let us move expensive tasks away from the UI thread.&lt;/p&gt;

&lt;p&gt;But getting an AI model to run once in a browser is very different from building a video editor that remains predictable during a long editing session.&lt;/p&gt;

&lt;p&gt;I recently completed a substantial infrastructure update to &lt;strong&gt;Timeline Studio&lt;/strong&gt;, a local-first browser AI video editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor/releases/tag/v1.0.2" rel="noopener noreferrer"&gt;v1.0.2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top" rel="noopener noreferrer"&gt;https://video-editor.ai-creator.top&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The release changed 37 files and added roughly 1,400 lines. The work focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stable dragging, splitting, reordering, and cross-track movement;&lt;/li&gt;
&lt;li&gt;synchronization between project time and media time;&lt;/li&gt;
&lt;li&gt;reusable timeline frame data;&lt;/li&gt;
&lt;li&gt;explicit high-performance WebGPU adapter selection;&lt;/li&gt;
&lt;li&gt;persistent AI workers and reusable inference sessions;&lt;/li&gt;
&lt;li&gt;centralized model caching through a service worker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article explains the engineering decisions behind that update.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A timeline is a constrained data model, not a row of draggable rectangles
&lt;/h2&gt;

&lt;p&gt;A first timeline prototype can position clips with a simple pixel conversion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pixelsPerSecond&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pixelsPerSecond&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dragging appears equally straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextStartTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;originalStartTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;deltaX&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;pixelsPerSecond&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A production clip, however, contains more than a visual position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;segment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;segment-001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trackId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visual-track&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trimStart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trimEnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourceDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;playbackRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single move may need to enforce all of these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the clip cannot begin before zero;&lt;/li&gt;
&lt;li&gt;it cannot exceed the project boundary;&lt;/li&gt;
&lt;li&gt;its position may snap to other edit points;&lt;/li&gt;
&lt;li&gt;a locked track cannot accept it;&lt;/li&gt;
&lt;li&gt;it may not overlap another clip on the same track;&lt;/li&gt;
&lt;li&gt;its media type must be compatible with the target track;&lt;/li&gt;
&lt;li&gt;related caption and audio state must remain valid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When each React component implements its own version of those rules, dragging, duplication, cutting, and reordering eventually disagree.&lt;/p&gt;

&lt;p&gt;The update moves timeline decisions into a shared domain layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pointer coordinates
       ↓
timeline coordinates
       ↓
candidate start time
       ↓
bounds and snapping constraints
       ↓
track routing and collision checks
       ↓
atomic project-state update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI collects input and renders a preview. Domain logic determines the final valid result.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. New clips must not rearrange existing work
&lt;/h2&gt;

&lt;p&gt;A common way to handle overlapping audio is to redistribute every clip until no overlap remains. The algorithm succeeds, but the user's earlier track arrangement changes unexpectedly.&lt;/p&gt;

&lt;p&gt;The new rule is deliberately asymmetric:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A new clip may search for an available track, but existing clips keep their lanes.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findTrackForNewAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newClip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;hasOverlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newClip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createAudioTrack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asset semantics also matter. AI-generated music belongs on the dedicated music track rather than whichever voice track happens to be empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveAudioTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ai-music&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MUSIC_TRACK_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;findAvailableVoiceTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A track is not merely a visual group. It may define mixing behavior, caption relationships, mute and solo behavior, volume defaults, and export rules.&lt;/p&gt;

&lt;p&gt;Automatic routing should therefore preserve both user intent and media meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Separate project time from media time
&lt;/h2&gt;

&lt;p&gt;A video editor has at least two clocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project time&lt;/strong&gt; describes where a clip appears in the final composition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media time&lt;/strong&gt; describes which position in the source file should be decoded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose seconds 10–20 of a source video are placed at seconds 30–40 of the project. At project time 33, the video element should display source time 13.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMediaTimeAtTimelineTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timelineTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timelineTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mediaTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trimStart&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="nx"&gt;localTime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;playbackRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trimStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trimEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mediaTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping this transformation independent from the DOM makes it reusable for trimming, splitting, playback-rate changes, clip movement, and multiple clips referencing the same source asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Do not write video.currentTime on every frame
&lt;/h2&gt;

&lt;p&gt;The obvious synchronization operation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this continuously forces the browser to seek repeatedly. That can cause decoder churn, visual jitter, black frames, and unnecessary CPU use.&lt;/p&gt;

&lt;p&gt;Instead, compare the current media position with the calculated target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;drift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSeeking&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;drift&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_ALLOWED_DRIFT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During normal playback, the media element advances on its own and small drift is tolerated. During timeline scrubbing, exact frame feedback matters more, so synchronization happens immediately.&lt;/p&gt;

&lt;p&gt;Both paths are called “synchronization,” but they optimize for different outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;playback optimizes for continuity;&lt;/li&gt;
&lt;li&gt;scrubbing optimizes for precision.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Make timeline frames part of the media asset
&lt;/h2&gt;

&lt;p&gt;Thumbnail frames are not just decoration. They help users identify content and find cut points.&lt;/p&gt;

&lt;p&gt;If those frames live only in temporary component state, they are easy to lose after splitting, copying, or moving a clip. A generated video may even appear as a blank block or one stretched cover frame.&lt;/p&gt;

&lt;p&gt;The asset now carries compact sampled frames:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoAsset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trackFrameDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trackFrames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same data can be reused by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the media-library card;&lt;/li&gt;
&lt;li&gt;the main visuals track;&lt;/li&gt;
&lt;li&gt;overlay tracks;&lt;/li&gt;
&lt;li&gt;multiple clips created from a split;&lt;/li&gt;
&lt;li&gt;browser-generated video assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rendering code selects frames based on the clip's trim range and visual width without rewriting the source frame data.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. WebGPU does not necessarily choose the fastest GPU
&lt;/h2&gt;

&lt;p&gt;A typical WebGPU setup begins with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAdapter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a dual-GPU machine, the browser may prefer an integrated adapter to save power. That can be reasonable for ordinary UI rendering but expensive for ONNX Runtime and generative workloads.&lt;/p&gt;

&lt;p&gt;For compute-heavy paths, the project now uses an explicit default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;powerPreference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high-performance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An explicit caller override must still win:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeAdapterOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;powerPreference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;powerPreference&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high-performance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides a high-performance default without breaking callers that intentionally request &lt;code&gt;low-power&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The policy is shared across AI music, speech, face processing, video repair, and super-resolution workers.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Handling requestAdapter calls inside third-party runtimes
&lt;/h2&gt;

&lt;p&gt;Not every adapter request is made by application code. A pinned runtime may internally call &lt;code&gt;requestAdapter()&lt;/code&gt; without options.&lt;/p&gt;

&lt;p&gt;When upgrading the dependency immediately would introduce compatibility risk, a narrowly scoped initialization wrapper can provide the missing default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalRequestAdapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestAdapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestAdapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;originalRequestAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;powerPreference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high-performance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details are essential:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the patch scoped to the relevant worker or initialization phase.&lt;/li&gt;
&lt;li&gt;Spread explicit options after the default so the caller retains control.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The original method should be restored after initialization. This is a compatibility strategy, not a preferred permanent API.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. AI latency is more than inference time
&lt;/h2&gt;

&lt;p&gt;The delay users experience usually contains several stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;download artifacts
       ↓
write cache
       ↓
read model files
       ↓
create inference sessions
       ↓
preprocess inputs
       ↓
run inference
       ↓
postprocess outputs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optimizing only model execution may leave most of the perceived delay untouched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Download independent artifacts in parallel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;artifacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;modelFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadModelFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create large GPU sessions serially
&lt;/h3&gt;

&lt;p&gt;Initializing several large WebGPU sessions at once can create a sharp memory and GPU-resource peak. Parallel downloading combined with serial session creation is usually a safer balance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep initialized workers alive
&lt;/h3&gt;

&lt;p&gt;Terminating a worker after every generation discards the expensive model sessions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;first use
   ↓
start worker
   ↓
initialize models
   ↓
generation 1
   ↓
generation 2
   ↓
release when the page closes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI should also distinguish &lt;strong&gt;model setup&lt;/strong&gt; from &lt;strong&gt;content generation&lt;/strong&gt;. A repeated generation should not look like another model download.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Give one component ownership of persistent model caching
&lt;/h2&gt;

&lt;p&gt;If the page, inference workers, and service worker all write to Cache Storage, a large model can be stored more than once.&lt;/p&gt;

&lt;p&gt;Timeline Studio makes the shared service worker the only persistent cache writer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inference worker
       ↓ model request
service worker
       ├─ normalize provider URLs
       ├─ resolve immutable model identity
       ├─ preflight storage capacity
       ├─ evict stale model families
       └─ write Cache Storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hugging Face and ModelScope may use different URLs for equivalent artifacts. Those URLs are normalized to one cache identity based on the model, immutable revision, and file path.&lt;/p&gt;

&lt;p&gt;A cache write failure also does not necessarily mean the current inference must fail. If the artifact is already available in memory, the task can continue; only the next session may need to download it again.&lt;/p&gt;

&lt;p&gt;That distinction separates a performance degradation from a functional failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Never expose “Failed to fetch” as the complete error
&lt;/h2&gt;

&lt;p&gt;A browser's generic &lt;code&gt;Failed to fetch&lt;/code&gt; message is nearly useless to an end user.&lt;/p&gt;

&lt;p&gt;The application layer should distinguish at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no network connection;&lt;/li&gt;
&lt;li&gt;an unavailable model mirror;&lt;/li&gt;
&lt;li&gt;insufficient browser storage;&lt;/li&gt;
&lt;li&gt;unavailable WebGPU support;&lt;/li&gt;
&lt;li&gt;model initialization failure;&lt;/li&gt;
&lt;li&gt;user cancellation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toUserFacingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNetworkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The model server is unavailable. Check your connection and retry.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isStorageError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Browser storage is full. This run will try an in-memory fallback.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isAbortError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The operation was cancelled.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The model could not be initialized.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful error should answer three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What failed?&lt;/li&gt;
&lt;li&gt;Can the current task continue?&lt;/li&gt;
&lt;li&gt;What can the user do next?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resulting architecture
&lt;/h2&gt;

&lt;p&gt;The refactored flow is easier to reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user interaction
       ↓
timeline commands and constraints
       ↓
project state
       ├─ segment placement
       ├─ track routing
       ├─ caption relationships
       └─ sampled track frames
                ↓
media synchronization
       ├─ project time
       ├─ source-media time
       └─ drift correction
                ↓
AI workers
       ├─ model lifecycle
       ├─ WebGPU adapter policy
       └─ cancellation
                ↓
centralized model cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important outcome is not the number of new features. It is the clearer separation of responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;timeline components no longer invent their own placement rules;&lt;/li&gt;
&lt;li&gt;media-time conversion is independent from UI state;&lt;/li&gt;
&lt;li&gt;WebGPU, worker lifecycle, and model caching form shared infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Validation
&lt;/h2&gt;

&lt;p&gt;The release was checked with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs ESLint, TypeScript checking, and the Vite production build. The release completed with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;zero ESLint errors;&lt;/li&gt;
&lt;li&gt;successful TypeScript validation;&lt;/li&gt;
&lt;li&gt;successful production build;&lt;/li&gt;
&lt;li&gt;GitHub release &lt;code&gt;v1.0.2&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;successful production deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final takeaways
&lt;/h2&gt;

&lt;p&gt;The difficult part of a browser-native AI video editor is not running a single model. It is keeping timeline state, media elements, React state, workers, GPU sessions, and local caches consistent throughout a real editing session.&lt;/p&gt;

&lt;p&gt;The most reusable lessons from this update are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Treat the timeline as a constrained domain model.&lt;/li&gt;
&lt;li&gt;Do not let new assets silently rearrange existing work.&lt;/li&gt;
&lt;li&gt;Separate project time from source-media time.&lt;/li&gt;
&lt;li&gt;Use different synchronization policies for playback and scrubbing.&lt;/li&gt;
&lt;li&gt;Explicitly request a high-performance WebGPU adapter for compute workloads.&lt;/li&gt;
&lt;li&gt;Download artifacts in parallel but initialize large GPU sessions serially.&lt;/li&gt;
&lt;li&gt;Reuse initialized workers and inference sessions.&lt;/li&gt;
&lt;li&gt;Give the service worker exclusive ownership of persistent model caching.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Browsers are becoming local compute platforms, not just document viewers. Turning those capabilities into a dependable creative tool requires careful boundaries around time, state, storage, and GPU resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor/releases/tag/v1.0.2" rel="noopener noreferrer"&gt;v1.0.2 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://video-editor.ai-creator.top" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>react</category>
      <category>webgpu</category>
    </item>
    <item>
      <title>Running Chinese TTS Fully in the Browser: Migrating from Piper to Kokoro 1.1 FP16</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Sat, 08 Aug 2026 09:22:18 +0000</pubDate>
      <link>https://dev.to/martindelophy/running-chinese-tts-fully-in-the-browser-migrating-from-piper-to-kokoro-11-fp16-4cpd</link>
      <guid>https://dev.to/martindelophy/running-chinese-tts-fully-in-the-browser-migrating-from-piper-to-kokoro-11-fp16-4cpd</guid>
      <description>&lt;p&gt;When an AI video editor needs text-to-speech, the easiest solution is usually a hosted API: send the script to a server, wait a few seconds, and download the audio.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio&lt;/a&gt;, we deliberately took a harder route. We want voice generation to run on the user's device, inside the browser, alongside the editable timeline.&lt;/p&gt;

&lt;p&gt;In Timeline Studio v1.0.0, we replaced our previous Chinese Piper/VITS ONNX voices with a browser-ready FP16 build of &lt;strong&gt;Kokoro multi-lang v1.1&lt;/strong&gt;. The new path provides two female and two male voices, handles Chinese text with inline English, and still performs synthesis locally through sherpa-onnx WASM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live editor:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;video-editor.ai-creator.top&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face Space:&lt;/strong&gt; &lt;a href="https://huggingface.co/spaces/haixin/timeline-studio" rel="noopener noreferrer"&gt;haixin/timeline-studio&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If browser AI, WebAssembly, ONNX, or open-source video editing interests you, a GitHub star is greatly appreciated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why move away from Piper for Chinese?
&lt;/h2&gt;

&lt;p&gt;Piper was a pragmatic starting point. Its ONNX models are relatively compact, the browser deployment path is well understood, and it allowed us to build a local TTS workflow without depending on a metered cloud service.&lt;/p&gt;

&lt;p&gt;But the quality bar changes when TTS becomes part of a video editor.&lt;/p&gt;

&lt;p&gt;It is no longer enough for a model to simply pronounce a sentence. Product demos, tutorials, explainers, and narrative videos need more natural pacing, clearer voice choices, and reliable handling of modern Chinese copy that often contains English names and technical terms.&lt;/p&gt;

&lt;p&gt;Consider this sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;使用 Timeline Studio，让 AI video editing 直接在浏览器里完成。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Splitting it into separate Chinese and English clips creates avoidable problems: speaker identity can change, pacing can jump, and the sentence loses its natural context. We wanted one speaker to generate the complete utterance.&lt;/p&gt;

&lt;p&gt;Kokoro multi-lang v1.1 gave us a better foundation for that experience.&lt;/p&gt;

&lt;p&gt;Piper has not disappeared from Timeline Studio. It still powers browser voices for German, Spanish, French, Italian, and Brazilian Portuguese. This migration only replaces the Chinese Piper route.&lt;/p&gt;

&lt;h2&gt;
  
  
  A browser model is more than an ONNX file
&lt;/h2&gt;

&lt;p&gt;Choosing a model was the easy part. Turning it into a dependable browser feature required much more work.&lt;/p&gt;

&lt;p&gt;A server can assume a controlled filesystem, abundant memory, a long-running process, and predictable model storage. A browser must deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first-use download size;&lt;/li&gt;
&lt;li&gt;memory and bandwidth pressure;&lt;/li&gt;
&lt;li&gt;main-thread responsiveness;&lt;/li&gt;
&lt;li&gt;storage quotas;&lt;/li&gt;
&lt;li&gt;interrupted or incomplete downloads;&lt;/li&gt;
&lt;li&gt;regional model availability;&lt;/li&gt;
&lt;li&gt;immutable model versions;&lt;/li&gt;
&lt;li&gt;cache migration across application releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A model that produces one successful sample on a developer machine is a demo. A product feature must download, verify, initialize, cache, reuse, upgrade, and fail clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting Kokoro 1.1 to FP16
&lt;/h2&gt;

&lt;p&gt;We converted the selected Kokoro multi-lang v1.1 bundle to FP16 and packaged it for the sherpa-onnx WASM TTS runtime.&lt;/p&gt;

&lt;p&gt;The resulting browser pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User script
    ↓
Chinese/English text normalization
    ↓
Voice ID → speaker ID
    ↓
Kokoro multi-lang v1.1 FP16
    ↓
sherpa-onnx WASM in a Web Worker
    ↓
Float32 PCM samples
    ↓
WAV encoding in the browser
    ↓
Timeline Studio asset library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FP16 reduces storage, transfer, and runtime memory-bandwidth pressure relative to FP32 while preserving the voice quality we need.&lt;/p&gt;

&lt;p&gt;It is not magic. The first run still requires a substantial model download, and synthesis speed depends on the device, browser, available memory, and script length. The goal was not to pretend the cost had disappeared, but to make a higher-quality multilingual TTS model practical in a local browser workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping inference off the main thread
&lt;/h2&gt;

&lt;p&gt;Model initialization and synthesis are compute-heavy operations. Running them on the main thread would make an editor feel frozen.&lt;/p&gt;

&lt;p&gt;Timeline Studio creates a dedicated worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/kokoro-multilang.worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;init&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;baseUrls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI thread handles the script, selected voice, progress display, and timeline state. The worker downloads and verifies the runtime bundle, initializes sherpa-onnx WASM, creates the offline TTS session, and generates the samples.&lt;/p&gt;

&lt;p&gt;A synthesis request is intentionally small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated sample buffer is returned as a transferable object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sampleRate&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sampleRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Transferring the underlying &lt;code&gt;ArrayBuffer&lt;/code&gt; avoids copying a potentially large block of PCM data.&lt;/p&gt;

&lt;p&gt;The worker stays alive after initialization, so repeated generations in the same editing session reuse the warm runtime instead of downloading and initializing the model again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four voices, one shared model
&lt;/h2&gt;

&lt;p&gt;We selected four speakers with distinct roles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Timeline Studio voice&lt;/th&gt;
&lt;th&gt;Upstream speaker&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qinglan / 晴岚&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zf_001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Natural, clear female voice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruoxi / 若溪&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zf_073&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Softer female voice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yunzhou / 云舟&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zm_009&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Steady, natural male voice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jingche / 景澈&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zm_010&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Younger, brighter male voice&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Internally, product voice IDs map to the four speaker slots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SPEAKER_IDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;zh_f_qinglan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;zh_f_ruoxi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;zh_m_yunzhou&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;zh_m_jingche&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All four voices share the same FP16 model. Switching speakers does not trigger another full model download.&lt;/p&gt;

&lt;p&gt;We also provide a real preview generated by the matching speaker for every selectable voice. A voice card should never play a placeholder sample from a different speaker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping mixed Chinese and English in one utterance
&lt;/h2&gt;

&lt;p&gt;Before synthesis, Timeline Studio normalizes the text while preserving Han characters, Latin characters, numbers, whitespace, and common punctuation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^\p&lt;/span&gt;&lt;span class="sr"&gt;{Script=Han}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Script=Latin}0-9&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;，。！？；：、,.!?;:()&lt;/span&gt;&lt;span class="se"&gt;\-]&lt;/span&gt;&lt;span class="sr"&gt;/gu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt; &lt;/span&gt;&lt;span class="se"&gt;\t]&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/ *&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;+ */g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;。&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;。&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;。&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important product rule is that mixed-language copy remains one linguistic utterance. We do not split a Chinese sentence simply because it contains "WebGPU", "API", or an English product name.&lt;/p&gt;

&lt;p&gt;That preserves speaker identity, punctuation-driven pauses, and editing simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parallel downloads with integrity checks
&lt;/h2&gt;

&lt;p&gt;The browser bundle includes the WASM binary, JavaScript runtime, wrapper code, model data, and supporting resources. Large data is divided into parts described by a manifest.&lt;/p&gt;

&lt;p&gt;The worker downloads manifest entries in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;fetchAndVerify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChunk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry includes its expected byte length and SHA-256 digest. The worker verifies individual downloads, reassembles the model data in manifest order, and verifies the completed payload again before creating the TTS session.&lt;/p&gt;

&lt;p&gt;A successful HTTP status is not enough. Proxies, partial caches, and interrupted connections can all return incomplete model data. Integrity verification prevents corrupted weights from reaching inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hugging Face and ModelScope mirrors
&lt;/h2&gt;

&lt;p&gt;Model availability is a product issue, especially for a browser application serving users in different regions.&lt;/p&gt;

&lt;p&gt;We mirror the voice bundle in repositories we control on both Hugging Face and ModelScope, pinned to immutable provider revisions.&lt;/p&gt;

&lt;p&gt;Timeline Studio prefers ModelScope for Chinese and domestic sessions and Hugging Face elsewhere. If the preferred route fails, it attempts the other mirror.&lt;/p&gt;

&lt;p&gt;The two providers use different URLs and revisions for the same artifacts, so we canonicalize them to one internal cache identity. Otherwise, a browser could store two copies of the same large model after a network route changes.&lt;/p&gt;

&lt;p&gt;The source may change; the model identity should not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache migration matters
&lt;/h2&gt;

&lt;p&gt;Timeline Studio also uses local models for captions, music, vision, voice conversion, and other features. Browser storage cannot be treated as unlimited.&lt;/p&gt;

&lt;p&gt;Before loading the Kokoro bundle, the application preflights available storage. During upgrades, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removes legacy FP32 Kokoro files;&lt;/li&gt;
&lt;li&gt;avoids duplicate Piper caches;&lt;/li&gt;
&lt;li&gt;migrates unchanged files to the new canonical revision;&lt;/li&gt;
&lt;li&gt;deletes changed manifest or model parts;&lt;/li&gt;
&lt;li&gt;evicts stale voice families when capacity is tight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not glamorous work, but it separates a one-release demo from a browser AI application that can continue evolving.&lt;/p&gt;

&lt;h2&gt;
  
  
  TTS is only the beginning of the workflow
&lt;/h2&gt;

&lt;p&gt;Timeline Studio is not a standalone text-to-speech page.&lt;/p&gt;

&lt;p&gt;A generated WAV becomes an editable media asset. The user can audition it, regenerate it, place it on the voiceover track, trim and move it, generate captions, mix it with source audio and music, and export the final project to MP4 or WebM.&lt;/p&gt;

&lt;p&gt;The current voice routing is deliberately model-specific:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language or use case&lt;/th&gt;
&lt;th&gt;Browser model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chinese and mixed Chinese/English&lt;/td&gt;
&lt;td&gt;Kokoro multi-lang v1.1 FP16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;English&lt;/td&gt;
&lt;td&gt;Kokoro 82M ONNX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;German, Spanish, French, Italian, Brazilian Portuguese&lt;/td&gt;
&lt;td&gt;Piper/VITS ONNX&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We prefer specialized, verified paths over claiming that one model is best for every language.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this migration delivered
&lt;/h2&gt;

&lt;p&gt;The final change included more than replacing two entries in a voice picker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kokoro multi-lang v1.1 replaced the Chinese Piper route;&lt;/li&gt;
&lt;li&gt;the model was converted and packaged as FP16;&lt;/li&gt;
&lt;li&gt;sherpa-onnx WASM runs synthesis locally;&lt;/li&gt;
&lt;li&gt;a persistent Web Worker keeps the editor responsive;&lt;/li&gt;
&lt;li&gt;two female and two male voices share one model;&lt;/li&gt;
&lt;li&gt;mixed Chinese/English scripts remain one utterance;&lt;/li&gt;
&lt;li&gt;model parts download in parallel and are SHA-256 verified;&lt;/li&gt;
&lt;li&gt;Hugging Face and ModelScope provide pinned fallback mirrors;&lt;/li&gt;
&lt;li&gt;both providers share one cache identity;&lt;/li&gt;
&lt;li&gt;storage preflight and cache migration support future releases;&lt;/li&gt;
&lt;li&gt;generated audio enters the editable video timeline instead of ending as a demo file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;Running AI in the browser is not simply a matter of moving an ONNX file to the frontend.&lt;/p&gt;

&lt;p&gt;A production-quality local feature needs a model delivery system, an isolated runtime, integrity checks, regional routing, cache ownership, upgrade behavior, honest progress reporting, and a clear place in the user's workflow.&lt;/p&gt;

&lt;p&gt;That engineering work is less visible than a model benchmark, but it is what turns local inference into a usable creative tool.&lt;/p&gt;

&lt;p&gt;Timeline Studio is open source under the MIT License:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;https://github.com/MartinDelophy/ai-video-editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try it online:&lt;/strong&gt; &lt;a href="https://video-editor.ai-creator.top/" rel="noopener noreferrer"&gt;https://video-editor.ai-creator.top/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this work is useful to you, please consider starring the repository, opening an issue, or contributing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From Natural Language to an Editable Video: Inside Timeline Studio's Agent Skill</title>
      <dc:creator>MartinDelophy</dc:creator>
      <pubDate>Fri, 07 Aug 2026 08:51:59 +0000</pubDate>
      <link>https://dev.to/martindelophy/from-natural-language-to-an-editable-video-inside-timeline-studios-agent-skill-4f08</link>
      <guid>https://dev.to/martindelophy/from-natural-language-to-an-editable-video-inside-timeline-studios-agent-skill-4f08</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;One instruction, one rendered video, and one editable &lt;code&gt;.timeline&lt;/code&gt; project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Repositories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Examples and reproducible workflows — start here
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MartinDelophy/timeline-studio-handbook" rel="noopener noreferrer"&gt;Timeline Studio Skills Handbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Handbook contains reference-video remakes, product promotions, multilingual localization, narrative marketing videos, and science explainers. Each case includes as much reproducible evidence as possible: the prompt, result video, editing decisions, and an editable &lt;code&gt;.timeline&lt;/code&gt; project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;MartinDelophy/ai-video-editor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main repository contains the browser video editor, Agent Skill, timeline command protocol, local AI integrations, and build and deployment configuration.&lt;/p&gt;

&lt;p&gt;Install the Skill with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add MartinDelophy/ai-video-editor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--skill&lt;/span&gt; edit-timeline-studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why video production needs more than another generation API
&lt;/h2&gt;

&lt;p&gt;Generative AI has made it easy to create text, images, speech, music, and captions independently. Producing one complete video is still a fragmented process.&lt;/p&gt;

&lt;p&gt;A short video may require all of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspecting and organizing source media;&lt;/li&gt;
&lt;li&gt;selecting useful shots;&lt;/li&gt;
&lt;li&gt;designing the narrative and pacing;&lt;/li&gt;
&lt;li&gt;generating or cleaning narration;&lt;/li&gt;
&lt;li&gt;creating and synchronizing captions;&lt;/li&gt;
&lt;li&gt;adding music, overlays, transitions, and effects;&lt;/li&gt;
&lt;li&gt;exporting and decoding the result;&lt;/li&gt;
&lt;li&gt;preserving a project that can still be edited later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most AI tools solve one item on this list. The creator still moves files between services and manually rebuilds timing relationships.&lt;/p&gt;

&lt;p&gt;Timeline Studio's &lt;code&gt;edit-timeline-studio&lt;/code&gt; Skill treats the entire process as an executable workflow. Its goal is not merely to produce an MP4. It also preserves an editable &lt;code&gt;.timeline&lt;/code&gt; project that a human can reopen and refine.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Starting from a natural-language brief
&lt;/h2&gt;

&lt;p&gt;The user describes the intended result rather than a sequence of UI operations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Turn these product demo clips into a roughly 40-second vertical promo. Preserve the real product interaction, add English narration and captions, and deliver both the video and an editable project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Skill decomposes that request into a controlled sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;inspect the named assets;&lt;/li&gt;
&lt;li&gt;analyze video, audio, speech, and text;&lt;/li&gt;
&lt;li&gt;record editorial decisions;&lt;/li&gt;
&lt;li&gt;build a declarative edit plan;&lt;/li&gt;
&lt;li&gt;apply timeline operations;&lt;/li&gt;
&lt;li&gt;run browser-local AI or rich editor features when required;&lt;/li&gt;
&lt;li&gt;validate the project and rendered output.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Natural-language brief
        ↓
Media analysis → Editorial decisions → Edit plan
        ↓
Transactional timeline operations
        ↓
AI speech, captions, music, and effects
        ↓
Project validation → Video validation
        ↓
MP4/WebM + editable .timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Skill is therefore more than a prompt template. It defines what the Agent must inspect, how it should execute the edit, and what evidence is required before the task can be called complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Analyze before cutting
&lt;/h2&gt;

&lt;p&gt;A common automatic-editing failure is to start cutting before understanding the media.&lt;/p&gt;

&lt;p&gt;Silence does not necessarily mean that a section is disposable. It may contain a product result, a useful reaction, intentional tension, or the preparation for an important action.&lt;/p&gt;

&lt;p&gt;Depending on the task, Timeline Studio can inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duration, resolution, frame rate, and audio presence;&lt;/li&gt;
&lt;li&gt;representative frames;&lt;/li&gt;
&lt;li&gt;speech transcripts and OCR text;&lt;/li&gt;
&lt;li&gt;shot-change evidence;&lt;/li&gt;
&lt;li&gt;subject position, scale, and motion;&lt;/li&gt;
&lt;li&gt;global and subject-region optical flow;&lt;/li&gt;
&lt;li&gt;audio-energy changes;&lt;/li&gt;
&lt;li&gt;visual clarity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The analysis is converted into a source-time decision record rather than immediately mutating the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assetId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product-demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceEnd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;19.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shorten"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keepStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;14.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keepEnd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;18.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Preserve the complete interaction and remove repeated setup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protectSourceAudio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes keep, remove, shorten, and reorder decisions explainable and reviewable.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Turning the brief into declarative timeline commands
&lt;/h2&gt;

&lt;p&gt;Once the plan is ready, the Agent uses stable project, track, asset, and clip identifiers wherever the command registry supports the requested operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.inspect /path/to/project.timeline
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.diff /path/to/edit-plan.json
npm run agent &lt;span class="nt"&gt;--&lt;/span&gt; project.run /path/to/edit-plan.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;project.inspect&lt;/code&gt; reads the project, tracks, clips, media, and captions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project.diff&lt;/code&gt; validates the plan and calculates a non-writing semantic diff.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project.run&lt;/code&gt; transactionally applies the validated plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified edit plan looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projectRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update-product-intro-v2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timed.move"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"clipId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"voice-intro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"caption.update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"clipId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"caption-intro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Let AI handle the repetitive editing work."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.8&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Making Agent edits safe
&lt;/h2&gt;

&lt;p&gt;Video editing needs many of the same guarantees as other stateful systems. A plan may update visuals, captions, narration, and music together. A partial result is often unusable.&lt;/p&gt;

&lt;p&gt;The command layer therefore uses several safeguards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revision checks:&lt;/strong&gt; reject a plan if the project changed after the plan was created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preconditions:&lt;/strong&gt; verify that clips, tracks, media, and expected state still exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions:&lt;/strong&gt; apply one user-visible intent as a complete unit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency:&lt;/strong&gt; use a stable &lt;code&gt;operationId&lt;/code&gt; so retrying a request does not duplicate media or captions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic diffs:&lt;/strong&gt; show meaningful project changes before writing the new archive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mechanisms separate a reliable production Agent from a UI automation demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Two execution paths
&lt;/h2&gt;

&lt;p&gt;Timeline Studio does not pretend that every editor feature is already a mature headless API. It uses two complementary paths.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Typical work&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Versioned command layer&lt;/td&gt;
&lt;td&gt;Project inspection, media import, clip timing, captions, track operations, portable archive output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser editor&lt;/td&gt;
&lt;td&gt;AI speech, automatic captions, advanced effects, digital humans, rich preview and full export&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Stable operations run through the command registry. Features that have not yet moved into that registry remain available through the local or hosted browser editor.&lt;/p&gt;

&lt;p&gt;After either path, the Skill reopens and verifies the project instead of assuming that a successful click or command means the edit is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why &lt;code&gt;.timeline&lt;/code&gt; matters
&lt;/h2&gt;

&lt;p&gt;Many AI video systems return only an MP4. That is a delivery file, not an editable source of truth.&lt;/p&gt;

&lt;p&gt;Timeline Studio uses a portable &lt;code&gt;.timeline&lt;/code&gt; archive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project.timeline
├── project.json
└── media/
    ├── visual-001.mp4
    ├── visual-002.png
    ├── voice-001.wav
    └── music-001.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project can preserve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the contiguous main Visuals track;&lt;/li&gt;
&lt;li&gt;timed picture-in-picture overlays;&lt;/li&gt;
&lt;li&gt;captions and stickers;&lt;/li&gt;
&lt;li&gt;voiceover, source audio, and music;&lt;/li&gt;
&lt;li&gt;transforms, masks, filters, animations, and keyframes;&lt;/li&gt;
&lt;li&gt;media identity and source-time mapping;&lt;/li&gt;
&lt;li&gt;canvas ratio and track state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An Agent can produce a first edit, while a human can continue working in a familiar visual timeline. Changing one caption or replacing one shot does not require regenerating the entire video.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Captions must map to audible speech
&lt;/h2&gt;

&lt;p&gt;Automatically generated videos often contain captions with missing, duplicated, or misaligned speech.&lt;/p&gt;

&lt;p&gt;Timeline Studio applies a simple invariant:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If captions are enabled, every visible caption must map to exactly one audible speech clip for its full interval.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Transcribed captions bind to existing source dialogue. Agent-authored explanation or narration receives a generated or recorded voice clip instead of becoming silent text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"captionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"caption-result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audioClipId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"voice-result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;18.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;22.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One instruction can produce a project you can still edit."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final verification checks caption boundaries, real audible output, duplicate narration, loudness differences, and unexpected stereo-channel offsets.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Browser-local AI
&lt;/h2&gt;

&lt;p&gt;Timeline Studio follows a local-first architecture. Supported inference runs in the browser with technologies including WebGPU, ONNX Runtime Web, Web Workers, Cache Storage, and WebCodecs.&lt;/p&gt;

&lt;p&gt;Current editor capabilities include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whisper Small Q8 ONNX automatic captions;&lt;/li&gt;
&lt;li&gt;Piper/VITS Chinese voices and Kokoro English voices;&lt;/li&gt;
&lt;li&gt;multilingual browser TTS;&lt;/li&gt;
&lt;li&gt;Stable Audio 3 Small Q4 ONNX music generation;&lt;/li&gt;
&lt;li&gt;YOLOS Tiny subject detection;&lt;/li&gt;
&lt;li&gt;MODNet portrait matting;&lt;/li&gt;
&lt;li&gt;MI-GAN object removal;&lt;/li&gt;
&lt;li&gt;NanoVSR image and video enhancement;&lt;/li&gt;
&lt;li&gt;vocal and accompaniment separation;&lt;/li&gt;
&lt;li&gt;JoyVASA and LivePortrait digital-human workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Models are loaded only when a feature needs them and are reused from browser caches. Owned Hugging Face and ModelScope mirrors can provide pinned model revisions while sharing a provider-independent cache identity where possible.&lt;/p&gt;

&lt;p&gt;Local-first execution reduces remote inference cost and helps keep unpublished media on the creator's device.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Completion means more than “export succeeded”
&lt;/h2&gt;

&lt;p&gt;A completed editing request normally produces both artifacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output/
├── result.mp4
└── result.timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before delivery, the Skill verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visuals continuity and clip ordering;&lt;/li&gt;
&lt;li&gt;transition and overlay timing;&lt;/li&gt;
&lt;li&gt;caption-to-speech relationships;&lt;/li&gt;
&lt;li&gt;the presence of real audible audio;&lt;/li&gt;
&lt;li&gt;output dimensions, duration, and container;&lt;/li&gt;
&lt;li&gt;full video decoding;&lt;/li&gt;
&lt;li&gt;the absence of repeated tails or stalled boundaries;&lt;/li&gt;
&lt;li&gt;successful reopening of the &lt;code&gt;.timeline&lt;/code&gt; archive;&lt;/li&gt;
&lt;li&gt;archived media resolution and first-frame preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The render and editable project must both be usable. A single opaque output file is not considered sufficient for a complete editing task.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Current boundaries
&lt;/h2&gt;

&lt;p&gt;The browser editor already provides multi-track editing, AI captions, speech, music, visual processing, and rich composition.&lt;/p&gt;

&lt;p&gt;The command runner can inspect and modify &lt;code&gt;.timeline&lt;/code&gt; projects and render its documented portable Visuals + Voiceover + Music subset. Rich captions, stickers, complex overlays, some effects, and AI generation may still require the browser editor.&lt;/p&gt;

&lt;p&gt;The current architecture is best summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stable command layer + browser compatibility layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This boundary is intentional. The project does not describe UI automation as deterministic headless execution, and the command registry can expand without blocking users from accessing editor features today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Timeline Studio's Agent Skill is not primarily about teaching an Agent to click through a video editor. It defines a production protocol for understanding media, recording editorial decisions, applying safe timeline mutations, and verifying the result.&lt;/p&gt;

&lt;p&gt;Its key ideas are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multimodal media analysis;&lt;/li&gt;
&lt;li&gt;content-specific editing workflows;&lt;/li&gt;
&lt;li&gt;declarative timeline operations;&lt;/li&gt;
&lt;li&gt;revision checks, transactions, and idempotency;&lt;/li&gt;
&lt;li&gt;browser-local AI inference;&lt;/li&gt;
&lt;li&gt;portable, editable &lt;code&gt;.timeline&lt;/code&gt; projects;&lt;/li&gt;
&lt;li&gt;validation of both the rendered video and the underlying project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI does not need to remove the creator from the process. It can remove the repetitive work while preserving human control over facts, taste, and final editorial decisions.&lt;/p&gt;

&lt;p&gt;Explore the reproducible examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MartinDelophy/timeline-studio-handbook" rel="noopener noreferrer"&gt;Timeline Studio Skills Handbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install, run, deploy, or contribute:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MartinDelophy/ai-video-editor" rel="noopener noreferrer"&gt;Timeline Studio main repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
