Time Model

How ef-timegroup controls timing, duration, and sequencing in a composition.

ef-timegroup is the core timing primitive. Every composition has at least one — the root group that defines the total video duration. Groups nest to build complex timelines.

duration

All time values are CSS time strings: 5s, 500ms, 2.5s.

mode="sequence"

Children play one after another. The group's total duration is the sum of its children's durations.

<ef-timegroup mode="sequence" id="root">
  <ef-timegroup mode="fixed" duration="2s" class="bg-indigo-900">
    <p>Scene 1 — 2s</p>
  </ef-timegroup>
  <ef-timegroup mode="fixed" duration="3s" class="bg-rose-900">
    <p>Scene 2 — 3s</p>
  </ef-timegroup>
  <ef-timegroup mode="fixed" duration="2s" class="bg-emerald-900">
    <p>Scene 3 — 2s</p>
  </ef-timegroup>
</ef-timegroup>

<style>
  #root {
    width: 1920px;
    height: 1080px;
    background-color: black;

    > ef-timegroup {
      display: grid;
      place-items: center;
      color: white;
      font-size: 6rem;
    }
  }
</style>

Sequence with overlapping transitions

Editframe timegroups support overlapping scenes — the foundation for transitions. Set overlap on a mode="sequence" to create a shared time window between adjacent scenes. Both scenes are active simultaneously during that window.

In order to use the overlap window for a transition, add CSS animations that reference the special timing variables --ef-transition-duration and --ef-transition-out-start.

To animate a transition into a scene, use --ef-transition-duration as the animation duration and start at 0s. To animate a scene out, use the same duration but start at --ef-transition-out-start to align with the end of the previous scene's overlap window.

<ef-timegroup mode="sequence" id="transitions" overlap="1s">
  <ef-timegroup mode="fixed" duration="2s" class="bg-indigo-900">
    <p>Scene 1 — 2s</p>
  </ef-timegroup>
  <ef-timegroup mode="fixed" duration="3s" class="bg-rose-900">
    <p>Scene 2 — 3s</p>
  </ef-timegroup>
  <ef-timegroup mode="fixed" duration="2s" class="bg-emerald-900">
    <p>Scene 3 — 2s</p>
  </ef-timegroup>
</ef-timegroup>

<style>
  @keyframes fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  @keyframes slide-out {
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
  }

  #transitions {
    width: 1920px;
    height: 1080px;
    background-color: black;

    > ef-timegroup {
      display: grid;
      place-items: center;
      color: white;
      font-size: 6rem;
      animation:
        /* Fade in animation */
        fade-in var(--ef-transition-duration) ease-out both,
        /* Slide out animation */
        slide-out var(--ef-transition-duration) ease-in var(--ef-transition-out-start) both;
    }
  }
</style>

mode="fixed"

Children overlap. Each element plays for its own duration, starting at an optional offset within the group.

<ef-timegroup mode="fixed" duration="6s" class="w-[1920px] h-[1080px] bg-black">
  <ef-video
    src="https://assets.editframe.com/bars-n-tone.mp4"
    duration="6s"
    class="absolute inset-0 size-full"
  >
  </ef-video>

  <!--
    Issues, over-reliance on tailwind for styling.
    Abosolute positioning isn't working as desired/expected. The two timegroups are stacking on top of each other in the top left corner, rather than respecting the specified positioning.
  -->
  <ef-timegroup 
    mode="fixed"
    duration="3s"
    offset="0.5s"
    class="absolute top-16 left-16 w-1/5 h-1/5 text-white text-5xl font-bold bg-black/50 px-4 py-2"
  >
    <p>appears at 0.5s</p>
  </ef-timegroup>

  <ef-timegroup
    mode="fixed"
    duration="3s"
    offset="3s"
    class="absolute bottom-32 left-16 w-1/5 h-1/5 text-yellow-300 text-5xl font-bold bg-black/50 px-4 py-2"
  >
    <p>appears at 3s
  </ef-timegroup>
</ef-timegroup>

offset shifts when an element begins within its parent fixed group.

mode="contain"

The group's duration matches the duration of the longest internal temporal element.

<!-- problems: over-reliance on tailwind for styling, should use agnostic style tag -->
<ef-timegroup mode="contain" class="w-[1920px] h-[1080px]">
  <ef-timegroup mode="fixed" duration="4s" class="absolute right-0 w-half h-full bg-indigo-900 flex items-center justify-center">
    <p class="text-white text-7xl font-bold">Scene A</p>
  </ef-timegroup>
  <ef-timegroup mode="fixed" duration="6s" class="absolute left-0 w-half h-full bg-rose-900 flex items-center justify-center">
    <p class="text-white text-7xl font-bold">Scene B</p>
  </ef-timegroup>
</ef-timegroup>

mode="fit"

The group's duration is set to be the duration of it's parent. This is useful for elements that should stretch to fill the entire composition, like a music track or background video.

This can be combined with a sequence to create a background track that automatically spans the full composition duration:

<ef-timegroup mode="contain" class="w-[1920px] h-[1080px]">

  <ef-timegroup mode="fit">
    <ef-audio src="https://assets.editframe.com/muted-guitar.mp3"></ef-audio>
  </ef-timegroup>

  <ef-timegroup mode="sequence">
    <ef-video
      src="https://assets.editframe.com/departure-prep.mp4"
      sourceout="4s"
    >
    </ef-video>

    <ef-video
      src="https://assets.editframe.com/window-seat.mp4"
      sourceout="4s"
    >
    </ef-video>

    <ef-video
      src="https://assets.editframe.com/landing.mp4"
      sourcein="8s"
      sourceout="12s"
    >
    </ef-video>
  </ef-timegroup>
</ef-timegroup>

Timegroups vs Temporal Elements

Timegroups are the typical element to use to structure the timing of your composition, but they aren't the only temporal elements.

  • <ef-video>
  • <ef-audio>
  • <ef-image>
  • <ef-text>

Can typically used as direct children of a timegroup. This can sometimes be desirable to simplify your composition structure.

But the direct use of temporal elements without wrapping timegroups reduces the flexibility of including non-temporal content, such as graphics, text, or other html elements to to lay out content with css and html.

loop

Add loop to the root ef-timegroup to loop the composition in the browser preview:

<ef-timegroup duration="5s" loop class="w-[1920px] h-[1080px] bg-black">
  ...
</ef-timegroup>

loop has no effect during rendering — the renderer plays through exactly once.

fps

fps controls the frame rate used when scrubbing and rendering. It defaults to 30. Drag the slider to see how frame rate affects animation smoothness:

<ef-timegroup id="root" mode="contain">

  <!-- The FPS for this timegroup is controlled by the slider -->
  <ef-timegroup mode="fixed" duration="6s"  fps="4">
    <p>FPS: 4</p>
  </ef-timegroup>

  <!-- The FPS for this timegroup is fixed at 30 -->
  <ef-timegroup mode="fixed" duration="6s"  fps="30">
    <p>FPS: 30</p>
  </ef-timegroup>

  <!--
    Each timegroup can have it's own FPS value.
    The output FPS will be the FPS of the ROOT timegroup.
    This allows you to have different apparent FPS for different
    parts of the composition.
  -->

</ef-timegroup>

<style>
  #root {
    width: 1920px;
    height: 1080px;
    background-color: #888888;
    display: flex;
    flex-direction: column;

    > ef-timegroup {
      height: 50%;
      position: static;
      display: grid;
      place-items: center;
    }
  }
  /*
   * In order to demonstrate the FPS, we need to apply an animation.
   * In Editframe, CSS animations are automatically syncronized to
   * the composition's timeline. This means that at 4 fps, the animation
   * will update every 250ms, and at 30 fps, it will update every ~33ms.
   */
  @keyframes slide {
    0% { transform: translateX(-25%); }
    50% { transform: translateX(25%); }
    100% { transform: translateX(-25%); }
  }
  p {
    animation: slide calc(var(--ef-duration) / 2) linear infinite;
    font-size: 200px;
    font-weight: bold;
    color: white;
  }
</style>