Turn SVG animations into video.
SMIL runs on the SVG's own clock. Pause it and set its time from a frame task on the timegroup, and the SVG follows every play, pause and seek.
Film outline
- Write the animation Steam over the Daybreak pour-over is drawn with SMIL <animate> and <animateTransform>. On its own, SMIL runs on the SVG's document clock and ignores the paused timegroup.
- Connect it with one frame task The page pauses the SVG and, in a timegroup frame task, calls setCurrentTime with ownCurrentTime. The SVG's gear meshes with the timegroup's and restarts at its time.
- Scrub, pause and seek Play, pause, drag back and forth, then click to seek: the steam lands on the frame the scrubber shows, and ownCurrentTime matches svg.getCurrentTime().
- Render the spot A cloud render loads the page and runs its frame task on every frame, so the steam renders with the Daybreak footage and headline to MP4.
Start with an HTML project
Scaffold the project, then add the SVG examples below to index.html:
Give the SVG the timegroup's clock
Every SVG runs its SMIL animations on its own clock, which starts when the page loads and ignores the timegroup. Call pauseAnimations() to stop that clock, then set its time from a frame task with setCurrentTime(). The SVG then follows every play, pause and seek. Each example below is wired this way.
setCurrentTime takes seconds, the same unit as ownCurrentTime. Cloud renders load the page and run the frame task for every frame they capture.
<ef-timegroup id="bars" mode="fixed" duration="4s" loop>
<svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg">
<rect x="110" y="300" width="60" height="20" fill="#1565C0">
<animate attributeName="height" values="20;260;20" dur="4s" repeatCount="indefinite"/>
<animate attributeName="y" values="300;60;300" dur="4s" repeatCount="indefinite"/>
</rect>
</svg>
</ef-timegroup>
<script type="module">
const timegroup = document.getElementById("bars");
const svg = timegroup.querySelector("svg");
svg.pauseAnimations();
timegroup.addFrameTask(({ ownCurrentTime }) => {
svg.setCurrentTime(ownCurrentTime);
});
</script><animate>
Five bars animate their height and y attributes on one 4 s cycle, each half a second behind the last.
<rect x="110" y="300" width="60" height="20">
<animate
attributeName="height"
values="20;260;20"
dur="4s"
begin="0s"
repeatCount="indefinite"
calcMode="spline"
keySplines="0.4 0 0.6 1;0.4 0 0.6 1"
keyTimes="0;0.5;1"
/>
<animate
attributeName="y"
values="300;60;300"
dur="4s"
begin="0s"
repeatCount="indefinite"
calcMode="spline"
keySplines="0.4 0 0.6 1;0.4 0 0.6 1"
keyTimes="0;0.5;1"
/>
</rect>
<!-- Each later bar starts half a second behind the last:
begin="-3.5s", "-3s", "-2.5s", "-2s" keep them in phase
when the 4s loop wraps --><animateTransform>
Two planets rotate around the sun, one every 3 s and one every 6 s.
<!-- Inner planet: 3s orbit -->
<g>
<animateTransform
attributeName="transform"
type="rotate"
from="0 320 180"
to="360 320 180"
dur="3s"
repeatCount="indefinite"
/>
<circle cx="410" cy="180" r="13" fill="#1565C0"/>
</g>
<!-- Outer planet: 6s orbit, 120° offset -->
<g>
<animateTransform
attributeName="transform"
type="rotate"
from="120 320 180"
to="480 320 180"
dur="6s"
repeatCount="indefinite"
/>
<circle cx="470" cy="180" r="20" fill="#C62828"/>
</g><animateMotion>
Three particles follow one path, a third of a lap apart.
<defs>
<path id="orbit-path" d="M 520 180 C 520 114 ..." />
</defs>
<!-- 3 particles, equally spaced (5s ÷ 3 = 1.667s apart) -->
<circle r="14" fill="#C62828">
<animateMotion dur="5s" begin="0s" repeatCount="indefinite">
<mpath href="#orbit-path"/>
</animateMotion>
</circle>
<circle r="10" fill="#1565C0">
<animateMotion dur="5s" begin="-1.667s" repeatCount="indefinite">
<mpath href="#orbit-path"/>
</animateMotion>
</circle>
<circle r="8" fill="#FFB300">
<animateMotion dur="5s" begin="-3.333s" repeatCount="indefinite">
<mpath href="#orbit-path"/>
</animateMotion>
</circle>