@editframe/elements
Web components SDK for building video compositions with Editframe.
Install
npm install @editframe/elements
Import the package once to register its custom elements:
import "@editframe/elements";
Playback speed and source trims
ef-video and ef-audio support playback-rate (JavaScript property
playbackRate). The default is 1; supported rates are 0.25 through 4,
inclusive. Audio keeps its pitch during playback and export.
<ef-configuration id="configuration">
<ef-timegroup id="cut" mode="fixed" duration="4s"
style="position: relative; width: 640px; height: 360px">
<ef-video id="clip" src="/take.mp4" sourcein="2s" sourceout="10s"
playback-rate="2" style="width: 100%; height: 100%"></ef-video>
</ef-timegroup>
</ef-configuration>
This plays source seconds 2 through 10 in four composition seconds.
sourcein/sourceout and trimstart/trimend refer to source time. Without
an explicit leaf duration, the trimmed source duration is divided by the
rate. An explicit leaf duration is in composition time and consumes
duration × playbackRate source seconds, capped by the available media.
Numeric seek times and currentTime are also in composition seconds.
Attach a controller explicitly for playback, then call play() from a user
gesture:
const configuration = document.querySelector("#configuration");
const cut = document.querySelector("#cut");
const playback = configuration.attachPlayback(cut);
// In a play button's click handler:
playback.play();
// Seeking during playback keeps the controller playing from the new time.
await playback.seek(1.5);
Invalid or missing playback-rate attributes resolve to 1. Assigning an
invalid playbackRate property value throws RangeError instead.
Audio fades
Set volumeAtTime as a JavaScript property on an audio or video element. It
receives clip-local composition seconds: 0 is the start of the clip,
regardless of its source trim or position in a parent timegroup. Return a
multiplier from 0 to 1; it is multiplied by the element's volume.
Finite values outside that range are clamped, and non-finite values use 1.
const clip = document.querySelector("#clip");
clip.volume = 0.8;
clip.volumeAtTime = (localTime) =>
Math.max(0, Math.min(1, localTime / 0.25, (4 - localTime) / 0.25));
This fades a four-second clip in and out over a quarter-second. Export evaluates the envelope for each output audio sample; playback smooths volume updates. Keep the callback deterministic so seeking and exporting produce the same envelope. A callback is not an HTML attribute and cannot be serialized into a saved markup string.
Export
Create an isolated render clone to export without seeking the live
composition. createRenderClone preserves volumeAtTime callbacks along
with the media attributes.
import { createRenderClone, renderTimegroupToVideo } from "@editframe/elements";
const handle = await createRenderClone(document.querySelector("#cut"), {
width: 640,
height: 360,
isolateMedia: true,
});
try {
const result = await renderTimegroupToVideo(handle.clone, {
width: 640,
height: 360,
fps: 30,
});
const mp4 = new Blob([result.buffer], { type: result.mimeType });
// Save or upload mp4 using your application's export flow.
} finally {
handle.dispose();
}
Export requires a browser with the relevant WebCodecs support. During development, a hot reload can leave custom elements from an older module instance; an export error beginning “Reload the page to export” means a full page reload is required.
Text capture preserves computed defaults such as line-height: normal,
including resets inherited from a parent. You can still set an explicit
line height for your design; 1.15 is not an SDK requirement. Font URLs in
stylesheets resolve relative to the stylesheet that declares them, so
those stylesheets and font files must be accessible to the rendering page.
See the documentation, composition skill, React bindings, and migration guide for more usage.