A loop is seamless when the frame after the last one is exactly the first one again. With a normal screen recording that’s luck. With web2vid it’s maths, because the clock is exact.
The rule
Pick a loop length T and make everything in the animation a function of t % T:
const T = 6000; // ms
function frame(t) {
const phase = (t % T) / T; // 0..1, exactly, on every frame
draw(phase);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
Then render with a duration of exactly T (6 seconds here). The last frame is at 5.967 s, and the next one would be 6.000 s, which is 0 again: no seam.
Things that move at different speeds
Make each speed a whole number of cycles per loop:
- something that turns once per loop:
angle = phase * 2π - something that pulses three times:
Math.sin(phase * 2π * 3) - something that scrolls one whole tile per loop:
offset = phase * tileWidth
If a speed doesn’t fit (2.5 cycles per loop), it will jump at the seam.
With CSS animations
Make the render duration a multiple of every animation’s duration. A 2 s @keyframes loop and a
3 s one both fit in a 6 s render. web2vid pauses and seeks CSS animations to the exact frame time,
so they line up perfectly.
Random, but repeatable
Math.random() gives different values every run, and a loop can’t jump. Use a seeded function of
something stable instead, for example an index:
const rnd = (n) => { const x = Math.sin(n * 127.1) * 43758.5453; return x - Math.floor(x); };
The hero animation on web2vid.com is built exactly like this.