3

Full JS from Github

Full css from Github

Full Html from Github

Video frames shake/stutter when rendering HTML5 video to Canvas during CSS animation

I'm experiencing video frame shaking/stuttering in a video carousel where videos are rendered to Canvas while a CSS animation is running. The videos should play smoothly but instead show visual jitter and frame drops.

Setup

I have a video carousel that:

  • Uses HTML5 <video> elements with Canvas rendering via drawImage()
  • Runs a continuous CSS animation (transform: translateY())
  • Uses IntersectionObserver to control video playback
  • Renders video frames using requestAnimationFrame

The Problem

Videos shake/stutter when the carousel animation is running. When I disable the CSS animation, videos play smoothly.

Relevant Code

Canvas Video Rendering:

function setupCanvasVideoPlayer(container, video) {
    const canvas = document.createElement('canvas');
    canvas.className = 'video-canvas';
    container.appendChild(canvas);
    
    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = false;
    
    let animationId;
    let isPlaying = false;
    
    const renderFrame = () => {
        if (!container.isConnected || !isPlaying) return;
        
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        try {
            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        } catch (e) {
            // Handle drawing errors
        }
        animationId = requestAnimationFrame(renderFrame);
    };
    
    video.addEventListener('play', () => {
        isPlaying = true;
        if (!animationId) {
            animationId = requestAnimationFrame(renderFrame);
        }
    });
}

CSS Animation:

.carousel-track {
    animation: scrollVideos 20s linear infinite;
    will-change: transform;
    backface-visibility: hidden;
    transform: translateZ(0);
}

@keyframes scrollVideos {
    0% { transform: translateY(0); }
    100% { transform: translateY(-1760px); }
}

HTML Structure:

<div class="right-carousel">
    <div class="carousel-track">
        <div class="carousel-video">
            <video src="video1.mp4" autoplay muted playsinline></video>
        </div>
        <div class="carousel-video">
            <video src="video2.mp4" autoplay muted playsinline></video>
        </div>
        <!-- More videos... -->
    </div>
</div>

What I've Tried

  1. GPU Acceleration optimizations:

    will-change: transform;
    backface-visibility: hidden;
    transform: translateZ(0);
    
  2. Canvas optimizations:

    ctx.imageSmoothingEnabled = false;
    // Using requestAnimationFrame for rendering
    
  3. Video element positioning:

    video.style.visibility = 'hidden';
    video.style.position = 'absolute';
    video.style.pointerEvents = 'none';
    

Questions

  1. Why does the CSS animation cause Canvas video rendering to shake?
  2. How can I synchronize Canvas rendering with CSS animations?
  3. Are there better approaches for smooth video playback in animated containers?
  4. Should I use transform3d() instead of translateY() for better performance?

Browser Testing

  • Chrome: Severe shaking
  • Firefox: Moderate shaking
  • Safari: Used Fallback Image

Expected Result

Smooth video playback without frame drops or visual stuttering during carousel animation.

Actual Result

Videos appear to shake/stutter, creating a poor user experience.


Environment:

  • Modern browsers (Chrome 120+, Firefox 115+)
  • Video format: MP4
  • Canvas 2D context
  • CSS animations + requestAnimationFrame

Any insights on resolving this Canvas + CSS animation conflict would be greatly appreciated!

3
  • 1
    How and where are you calling your function? Is it just a matter of looping through div.carousel-video, and calling the function for each container-video pair? Does the problem persist if you try a different device (other desktop/laptop, a tablet, a mobile phone)? Commented Jun 21 at 7:22
  • Thanks for the questions! Function calling: Yes, exactly - I loop through all .carousel-video divs and call setupCanvasVideoPlayer() for each container-video pair via IntersectionObserver:document.querySelectorAll('.carousel-video').forEach(container => { const video = container.querySelector('video'); if (video) { videoObserver.observe(container); } }); The function gets triggered when videos enter the viewport. Device testing: The problem persists across different devices - severe shaking on desktop Chrome, moderate on Firefox. Commented Jun 21 at 17:04
  • Looks ok for me in Chrome 137 on macOS: jsfiddle.net/k4qhLz2x One thing you could try: detach your <video> elements from the DOM. Browsers do throttle video elements that are in the DOM but not visible on screen (though that would be weird I don't experience it on my setup). Commented Jun 23 at 0:42

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.