Video editing without an NLE
Trim, splice, cross-fade, burn-in captions — all from one HTML document, version-controlled like code.
Ship a cross-faded 3-clip edit in under five minutes, using nothing but an HTML file and the hyperframes CLI. No timeline. No project file. Just markup that diffs cleanly in git.
What you'll learn
- How
data-start,data-duration, anddata-trim-startreplace dragging clips around - How
data-fadedoes cross-fades without a transition panel - How to splice three clips into one MP4 from a single document
The result
The model
Every clip is a DOM element. Three data attributes carry the timing:
data-start— when this clip enters the timeline (seconds)data-duration— how long it staysdata-trim-start— offset into the source if you're pulling from a longer asset
For cross-fades, add data-fade="in:0.4,out:0.4" and overlap two clips by the fade length. That's the whole edit grammar.
A three-clip splice
Here is the full composition. Notice there is no JavaScript at all — CSS animations driven by frame-pinned time do the work.
<!doctype html>
<html>
<head>
<style>
body { margin:0; background:#000; color:#fff; font: 600 72px/1 ui-sans-serif, system-ui; }
.clip { position:absolute; inset:0; display:grid; place-items:center; opacity:0; }
.intro { background:#0f172a; }
.middle { background:#7c2d12; }
.outro { background:#052e2b; }
@keyframes fade { 0%,100% {opacity:0} 10%,90% {opacity:1} }
.intro { animation: fade 3s linear 0s both; }
.middle { animation: fade 4s linear 2.6s both; }
.outro { animation: fade 3s linear 6.2s both; }
</style>
</head>
<body>
<div class="clip intro" data-start="0" data-duration="3" data-fade="out:0.4">One.</div>
<div class="clip middle" data-start="2.6" data-duration="4" data-fade="in:0.4,out:0.4">Two.</div>
<div class="clip outro" data-start="6.2" data-duration="3" data-fade="in:0.4">Three.</div>
</body>
</html>Trimming a source clip
If a clip element pulls from a longer asset (a video, a long CSS keyframe sequence), data-trim-start shifts the playhead inside it without changing where it lands on the timeline.
<video class="clip"
src="raw-interview.webm"
data-start="0"
data-duration="6"
data-trim-start="12.5"></video>That single attribute is the difference between "I cropped the clip" and "I re-encoded the source." Renders stay deterministic because the offset is part of the input, not a runtime decision.
Burn-in captions
Captions are just spans with the same timing grammar. They render into the MP4 by virtue of being on the page when the frame is captured.
<p class="caption" data-start="2.8" data-duration="3.4">
Edits should diff like code.
</p>No SRT, no muxing step, no font-loading flake — the caption is literally a DOM node.
Tweak it
- Swap
data-fadefor a CSSclip-pathtransition for a wipe instead of a cross-fade. - Add
data-easing="cubic-bezier(.2,.8,.2,1)"to make the fade feel less linear. - Use
hyperframes inspect composition.htmlto print the resolved timeline as JSON before you render.