When Time Becomes a Data Model
For OpenCut, time is not merely a label under a preview. It determines which frame is visible, where a keyframe takes effect, how a speed change alters duration, whether audio stays aligned, and what an export produces. This chapter examines the former editor's move toward an explicit time model and a Rust/wgpu compositor behind a WebAssembly boundary. The goal is to understand both the leverage of that design and the limits of treating a historical implementation as proof of what the rewrite does today.
Why seconds are not enough
People naturally describe a video as lasting a number of seconds. Software can do the same, but a video editor eventually has to answer a more exact question: which frame belongs at a particular position on the timeline? That question becomes difficult when the timeline uses frame rates that are close to, but not exactly, whole numbers; when an effect or animation must land on a frame boundary; or when audio, playback, and export use the same project clock.
The 0.3 technical notes say that the former editor replaced floating-point seconds with MediaTime, an integer tick count at 120,000 ticks per second. They also describe FrameRate as a rational pair of a numerator and denominator rather than as a floating-point value. In plain terms, the system stores time as countable units and describes a frame rate as a fraction. The exact names belong to the historical implementation, but the underlying design is easy to grasp: do not ask a binary approximation to decide whether two frame boundaries are the same when the domain itself has an exact structure.
The release notes give concrete mappings. At 23.976 frames per second, a frame corresponds to 5,005 ticks. At 29.97, it corresponds to 4,004 ticks. At 30, it corresponds to 4,000 ticks. Those values are not decorative constants. They provide a common unit for advancing the playhead, snapping an edit, formatting timecode, and comparing positions. A trim or keyframe can be represented at a boundary that the renderer and the timeline recognize in the same way.
This matters especially for rates that use a denominator such as 1001. A decimal display like 29.97 is a convenient human shorthand, not the complete identity of the rate. If one part of an application treats it as a rounded decimal while another treats it as a frame fraction, small errors can accumulate as the sequence grows. The former release notes explicitly say that floating-point seconds had accumulated rounding errors and made frame alignment imprecise. The integer-time and rational-rate design addresses that failure at the representation level rather than trying to repair every visible symptom afterward.
The tick examples make the difference concrete. A 30-frame sequence advances through boundaries separated by 4,000 ticks; a 29.97 sequence advances by 4,004 ticks per frame. The application can compare those integer positions directly instead of repeatedly converting a displayed decimal back into a frame decision. A user still sees familiar timecode, but the internal question is more precise: is this operation at the same domain boundary as the frame, keyframe, or neighboring clip? The display remains a convenience layer; the tick count is the shared reference used by operations.
That choice also limits what the time model promises. It can make a frame boundary representable and comparable, but it cannot decide whether a trim should ripple other clips, whether an audio source can change speed without artifacts, or whether a browser can render the result fast enough. Those are policies and runtime behaviors built on top of the clock. A precise representation prevents one category of disagreement; it does not remove the need for explicit editing rules or a scheduler that handles work arriving late.
The benefit is not limited to the timeline ruler. Consider an animated position. A keyframe is attached to a time, and an easing curve determines the value between two times. If the playhead lands just before or after a boundary because of inconsistent rounding, the preview can show a different value from the inspector or from an export. The same issue appears when a user snaps a clip to another clip, expands animated properties into their own lanes, or copies keyframes between elements. All of those interactions rely on a shared definition of “at this time.”
Audio makes the clock harder to ignore. The release history describes speed control for video and audio, including an option to maintain pitch, as well as RMS-based audio waveforms that are properly scaled and render only the visible region. A speed change changes the relationship between source time and project time. A waveform must be interpreted against that relationship, and playback must schedule audio without losing continuity. The earlier release also documents a scheduler that had to recover from timing slips instead of allowing audio to flicker or cut out.
Export is the final observer of the same model. If the preview uses one conversion from project time to media time and the exporter uses another, a project can look correct while the output has a shifted cut, a missing frame, or an audio mismatch. The release notes report a fix for MP4 exports with audio that failed in Firefox, but they do not provide a complete browser matrix or independent measurements. The safe conclusion is narrower and more useful: the product history shows that browser, audio, timing, and export boundaries were real engineering concerns, and a common time model was intended to reduce one class of disagreement among them.
A time model therefore has three jobs. It represents positions without avoidable drift. It gives operations such as snapping, trimming, animation, and speed changes a stable reference. And it lets different consumers—interactive playback, waveform drawing, preview rendering, and export—ask the same question and receive compatible answers. It does not solve scheduling, decoding, or user interaction by itself. It makes those systems coordinate around a less ambiguous foundation.
One boundary for pixels, and its conditions
The former editor's rendering architecture followed a related direction. The 0.3 notes say that its WebGL renderer was replaced by a Rust/wgpu compositor compiled to WebAssembly. They name separate areas for compositing, effects, masks, GPU context, and time; state that mask feathering used a jump-flooding approach; and say that WebAssembly bindings were consumed from TypeScript. This places a computation boundary between the editor surface and the code that turns a scene into pixels.
That boundary has a straightforward rationale. The web interface is good at presenting controls, handling browser events, and displaying application state. Rendering a layered scene, applying effects, feathering masks, and keeping time calculations consistent are more specialized operations. Putting those operations behind one Rust/WASM-facing layer can give the web editor one place to request a result rather than requiring separate JavaScript implementations for preview, effects, and time-sensitive behavior.
The relationship is easier to see as a sequence:
This is a conceptual account of the historical boundary, not a diagram of an already integrated rewrite. The important control flow is that the editor surface should not invent its own frame arithmetic after handing work to the compositor. Time information crosses into the Rust/WASM layer; the compositor crosses into the GPU or browser capability; the result crosses back to a preview or export consumer. If those boundaries use incompatible scene or time representations, centralization has not achieved much.
A shared compositor can also make feature composition more tractable. A mask is not simply a boolean switch. It changes which pixels of a video or image layer survive, and its position, size, rotation, feather, and stroke interact with the layer's geometry. An effect changes the pixels produced for a clip or a standalone timeline element. A background or blend mode changes how layers combine. If these operations are implemented in one rendering path, the preview and export have a better chance of applying the same sequence. The release notes establish the direction and the named subsystems; they do not prove that every output path was identical or that the rewrite has retained the same code.
The renderer also sits at the end of a dependency chain. Before it can compose a frame, the application must know which layers are active, where each layer is in time, which properties are animated, and which effects or masks apply. The compositor can then turn that description into pixels, but it should not silently invent missing timeline semantics. After composition, the preview needs to show the result quickly enough for interaction, while export may need a different pace but the same scene meaning. Keeping the shared description stable is therefore as important as moving the pixel work into a faster language.
This helps explain why a rendering migration can be valuable without being sufficient. It can centralize expensive or precision-sensitive work and give multiple consumers a common computation path. It cannot, by itself, define the project format, settle ownership of edits, or guarantee that interactive and batch consumers pass identical inputs. Those are integration contracts that the future core would have to make explicit.
The trade-off is equally important. Centralizing precision-sensitive work can reduce duplicated rules, but it creates a boundary that must be maintained. TypeScript controls, Rust values, WebAssembly bindings, and GPU resources all need compatible lifecycles. A browser without GPU-accelerated rendering can become an operating-condition failure rather than a minor performance variation. OpenCut's former release notes say the editor now displayed a notice explaining that limitation and suggested another browser. That is a product-level admission that the rendering design depends on capabilities outside the application.
The current rewrite makes this limit visible by contrast. Its web project has a TypeScript/TanStack/Vite surface and Cloudflare deployment configuration, while the visible root Rust workspace contains the desktop application and does not register the historical core-crate tree. The current web editor route remains a placeholder. Thus, the repository supports two separate statements: OpenCut previously documented a Rust/wgpu/WASM rendering architecture in its classic line, and the rewrite is being positioned around a Rust core. It does not support the stronger statement that the current rewrite already has that compositor wired into its editor.
This distinction also prevents a common architectural mistake: assuming that a lower-level implementation automatically guarantees portability. WebAssembly can provide a useful bridge to the browser, and a native Rust foundation may be reusable on desktop, but the surrounding environment still supplies different GPU APIs, windowing behavior, memory limits, and failure modes. The source describes a GPU fallback notice for the former web editor and an early GPUI desktop application for the rewrite. Those facts point toward a shared computational center with platform-specific edges, not toward a single binary that eliminates all platform work.
The most defensible lesson is that OpenCut's time and rendering work belong to the same architectural story. A stable clock gives surfaces a common temporal language. A compositor boundary gives them a common way to turn a scene into pixels. Together they could support interactive editing, export, and future automation more reliably than a set of loosely related UI handlers. But the foundation becomes real only when current surfaces can submit meaningful scenes, receive consistent results, and recover when the browser or GPU cannot satisfy the request.
Even that is not enough for a timeline editor. Rendering can show a frame, but it does not decide how a trim affects neighboring clips, what tracks are legal, or how an edit should be reversed. Those are state-transition rules. The next layer of OpenCut's architecture is therefore the timeline itself: the point where a user's gesture becomes an explicit change to a structured scene.