Skip to main content

February 23, 2026

How to Add a Confetti Animation When a Vimeo Video Starts Playing

5 min read

confetti over Vimeo Video playing

There's something about confetti that just works. It's celebratory, unexpected, and when it's timed right, it feels like the page is reacting to something the user did. Not just decoration sitting on a screen.

This post walks you through how to trigger a canvas confetti animation the moment a Vimeo video starts playing. We'll use the Vimeo Player API to hook into the play event, wait a beat, then fire the confetti. The result feels intentional and polished rather than random.

If you haven't worked with the Vimeo Player API before, I wrote a full breakdown of the basics here. This post will cover enough to get you up and running on its own, but that one goes deeper on the fundamentals.

What You're Building

A Vimeo video plays. About a third of a second later, confetti bursts across the screen. That's it. Simple concept, but the implementation has a few moving parts worth understanding.

Here's what you need:

  • A Vimeo video embedded on your page
  • The Vimeo Player SDK loaded via CDN
  • The canvas-confetti library loaded via CDN
  • A small JavaScript block wiring them together

Loading Your Dependencies

Add both of these script tags before your closing tag. Order matters here. Both need to be available before your script runs.

Setting Up Your Vimeo Iframe

Place your Vimeo iframe on the page and give it an ID. That ID is how your JavaScript will find it.

<iframe
  id="my-video"
  src="https://player.vimeo.com/video/YOUR_VIDEO_ID?h=YOUR_HASH"
  frameborder="0"
  allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media"
  allowfullscreen>
</iframe>

If you want the video to autoplay when the page loads, add muted=1&autoplay=1 to the URL. Browsers block autoplay with sound by default, so muted is required for it to work.

Creating the Player Instance

Once the SDK is loaded and the iframe is on the page, you create a player instance by passing the iframe element to Vimeo.Player:

const iframe = document.getElementById('my-video');
const player = new Vimeo.Player(iframe);

You now have a JavaScript object that can communicate with the embedded video.

Hooking Into the Play Event

The Vimeo Player API fires a play event every time the video starts or resumes playing. You listen for it using player.on():

player.on('play', function() {
    // this runs every time the video plays
});

This is the hook. Instead of guessing when the video starts or using a timer from page load, you're reacting to the actual moment playback begins. That's what makes the timing feel right.

Why a 300ms Delay Before the Confetti

You could fire the confetti the instant the play event fires. But in practice, firing it immediately can feel slightly off. The video frame is just starting to render, the browser is doing a few things at once, and the confetti ends up competing for attention before the video has had a chance to settle.

A 300ms delay gives the video just enough time to actually start showing on screen. By the time the confetti fires, the video is visible and playing, so the celebration feels like a reaction to something rather than something that happened alongside it. It's a small thing but it makes a noticeable difference to how polished the interaction feels.

player.on('play', function() {
    setTimeout(function() {
        confetti({
            particleCount: 150,
            spread: 80,
            origin: { y: 0.4 },
            zIndex: 9999
        });
    }, 300);
});

Controlling the Confetti

The canvas-confetti library gives you a few key options worth knowing:

particleCount controls how many confetti pieces fire. 150 is a solid burst without being overwhelming. Crank it up to 300 for something more dramatic, drop it to 80 for something subtle.

spread controls how wide the burst fans out. 80 gives you a natural arc. Higher numbers spread it wider across the screen.

origin controls where the burst comes from. The default is the center of the screen. { y: 0.4 } pushes it slightly above center, which tends to look better when there's content below it.

zIndex makes sure the confetti renders on top of everything else on the page. 9999 is a safe bet.

One Thing to Watch Out For

The play event fires every time the video plays, not just the first time. If your user pauses and resumes the video, the confetti fires again. For a lot of use cases that's fine. But if you only want it to fire once, you can remove the confetti listener after the first time it runs.

The way to do that is with player.off(). When you register a listener with player.on(), Vimeo stores a reference to that specific function internally. player.off() tells Vimeo to find that function and deregister it. After that, the event still fires when the video plays, but there's nothing listening for it anymore.

For this to work, the function needs a name. If you write it as an anonymous function, you have nothing to pass to player.off() and no way to remove it. By naming it onPlay, you can pass that same reference to both player.on() and player.off():

function onPlay() {
    setTimeout(function() {
        confetti({
            particleCount: 150,
            spread: 80,
            origin: { y: 0.4 },
            zIndex: 9999
        });
    }, 300);

    // Remove this listener so confetti only fires once
    // The video will still play normally — this only stops the confetti
    player.off('play', onPlay);
}

player.on('play', onPlay);

The comments in the code do the heavy lifting here. Anyone reading this later knows exactly what player.off() is doing and why it's there. Without them, it reads like a line that came from nowhere.

The Full Code

Here's everything together in one clean block. If you're working in Webflow, add the two CDN script tags and this script block before your closing tag:

Clean, readable, and the confetti only fires once no matter how many times the user interacts with the video.

window.Webflow = window.Webflow || [];
window.Webflow.push(function() {

    const iframe = document.getElementById('my-video');
    const player = new Vimeo.Player(iframe);

    function onPlay() {
        setTimeout(function() {
            confetti({
                particleCount: 150,
                spread: 80,
                origin: { y: 0.4 },
                zIndex: 9999
            });
        }, 300);

        // Remove this listener so confetti only fires once
        // The video will still play normally — this only stops the confetti
        player.off('play', onPlay);
    }

    player.on('play', onPlay);

});
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js"></script>
<script src="https://player.vimeo.com/api/player.js"></script>

This Is the Kind of Detail That Sticks

Confetti is small. But small interactions done well are what separate a website that feels professionally built from one that just looks professionally built.

The Vimeo Player API makes it possible to react to what's actually happening in the video rather than guessing with timers. Combined with canvas-confetti, you can add moments of delight that feel earned rather than random.

If you want to go deeper on the Vimeo Player API itself, including how to control multiple videos on the same page, read How to Use the Vimeo Player API to Control Video on Your Website.

If you're building something like this for your business and want to talk through the approach, I offer free strategy sessions. We can look at what you're working on and figure out the best way to bring it to life. Book a call here.

marketing health check clipboard showing score

Your Marketing Isn't Bringing in Leads and You Can't Figure Out Why

Score your marketing across 7 key areas in 5 minutes and find out exactly what's holding back your leads and what to fix first.

Questions You Might Be Asking

No. The Vimeo Player SDK works on free accounts and on public, unlisted, and password-protected videos alike. You only need a paid plan if you want to remove Vimeo branding, restrict embedding to specific domains, or use advanced analytics features. The play event and everything in this tutorial work on any plan.

canvas-confetti renders thousands of independent particles with physics on the GPU, which is much cheaper than animating that many DOM elements or layered Lottie frames. It's also tiny — about 3KB gzipped — and configurable via a single function call. CSS keyframes struggle to look organic at scale, and Lottie is overkill when you don't need a designed composition.

Create a separate Vimeo.Player instance for each iframe and attach its own onPlay handler with whatever confetti configuration you want. Each player listens independently, so one video can fire a small burst from the bottom while another fires a wide overhead shower. Keep the handler functions named so you can detach them individually with player.off() later.

Out of the box, no — canvas-confetti doesn't check prefers-reduced-motion automatically. Wrap your confetti call in a matchMedia check: if window.matchMedia('(prefers-reduced-motion: reduce)').matches is true, skip the burst or swap it for something subtler. It's a two-line change and keeps the experience accessible for anyone who has motion sensitivity.

Stop losing deals to a website that's holding you back.

Book a 30-minute strategy session. You'll leave with a clearer picture of what your site needs to do and a rough budget range, whether you hire us or not.

background cta image collage of websites

Other posts in this category

A three-way comparison graphic: three website mockups side by side labeled DIY Builder, Template, and Custom, each with a small price tag and a "best when" note beneath it, and an arrow underneath showing the progression as a business grows.

Is a Custom Website Worth It, or Should I Use a Template or DIY Builder?

Custom, template, or DIY builder? Here's how the three options compare, plus a simple decision framework.

vimeo logo against a blue background

How to Use the Vimeo Player API to Control Video on Your Website

Learn how to use the Vimeo Player API to control embedded video with JavaScript. This guide covers setup, play/pause, promises, events, and common pitfalls.

Scroll into view thumbnailscro

How to Use scrollIntoView() in Webflow (With Sticky Header Offset and Smooth Easing)

Webflow's built-in anchor links fall apart the moment you have a sticky header. This script gives you smooth scrolling, precise alignment, and per-section offset control with one drop-in code block.