Add Confetti effect to Lightning components and Flows

We all love celebrating! And no celebration is complete without some Confetti in it. In Summer’19, Salesforce introduced Confetti celebration in Lightning Path.
https://releasenotes.docs.salesforce.com/en-us/summer19/release-notes/rn_sales_features_core_path.htm

But what if I tell you we can toss this confetti at a lot more places than just Lighnting Path? Sounds exciting, right?

But how do we do that?

Well, there is a JavaScript file which you can use in your Lightning Component as static resource and spread some love with that Confetti in your Salesforce org.
Link: https://cdn.jsdelivr.net/npm/canvas-confetti@0.2.1/dist/confetti.browser.min.js

I created a Lightning component to demo few of the confetti effects that we can do with this JavaScript file.

Code

showConfettiCMP.cmp

<aura:component implements="lightning:availableForFlowScreens,force:lightningQuickAction">
    
    <aura:attribute name="colors" type="String[]" default="['#610B0B','#FFFF00','#FF00BF','#0040FF','#585858','#00FFBF','#FE642E','#FFBF00','#0101DF','#FF8000','#00FF00','#FF0040','#A901DB','#0B0B3B','#FF0000']"/>
    
    <ltng:require scripts="{!$Resource.Confetti}"/>
    
    <div class="slds-align_absolute-center">
        <lightning:layout horizontalAlign="spread" verticalAlign="stretch" multipleRows="true" >
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6"  flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Basic Cannon" onclick="{!c.basicCannon}"/>
            </lightning:layoutItem>
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6" flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Fireworks" onclick="{!c.fireworks}"/>
            </lightning:layoutItem>
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6" flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Confetti Shower" onclick="{!c.confettiShower}"/>
            </lightning:layoutItem>
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6" flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Winner Celebration" onclick="{!c.winnerCelebration}"/>
            </lightning:layoutItem>
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6" flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Burst Mode" onclick="{!c.busrtMode}"/>
            </lightning:layoutItem>
            <lightning:layoutItem class="slds-align_absolute-center" size="6" smallDeviceSize="6" mediumDeviceSize="6" largeDeviceSize="6" flexibility="auto" padding="around-small">
                <lightning:button variant="brand" class="buttonBox" label="Canvas Confetti" onclick="{!c.canvasConfetti}"/>
            </lightning:layoutItem>
        </lightning:layout>
    </div>
    <br/>
        
    <canvas id="customCanvas" aura:id="customCanvas" class="canvasCss">
    </canvas>
       
</aura:component>

showConfettiCMPController.js

({
    basicCannon : function(component, event, helper) {
        confetti({
            particleCount: 200,
            startVelocity: 60,
            spread: 150,
            origin: {
                y: 0.9
            },
            colors : component.get("v.colors")
        });
    },
    
    fireworks : function(component, event, helper){
        var end = Date.now() + (15 * 100);
        
        var interval = setInterval(function() {
            if (Date.now() > end) {
                return clearInterval(interval);
            }
            
            confetti({
                particleCount : 450,
                startVelocity: 30,
                spread: 360,
                ticks: 60,
                origin: {
                    x: Math.random(),
                    // since they fall down, start a bit higher than random
                    y: Math.random() - 0.2
                },
                colors : component.get("v.colors")
            });
        }, 200);
    },
    
    confettiShower : function(component, event, helper){
        var end = Date.now() + (15 * 100);
        
        (function frame() {
            confetti({
                particleCount: 10,
                startVelocity: 0,
                ticks: 300,
                origin: {
                    x: Math.random(),
                    // since they fall down, start a bit higher than random
                    y: 0
                },
                colors: component.get("v.colors")
            });
            
            if (Date.now() < end) {
                requestAnimationFrame(frame);
            }
        }());
    },
    
    winnerCelebration : function(component, event, helper) {
        var end = Date.now() + (15 * 100);
        
        (function frame() {
            confetti({
                particleCount: 10,
                angle: 60,
                spread: 25,
                origin: {
                    x: 0,
                    y : 0.65
                },
                colors: component.get("v.colors")
            });
            confetti({
                particleCount: 10,
                angle: 120,
                spread: 25,
                origin: {
                    x: 1,
                    y : 0.65
                },
                colors: component.get("v.colors")
            });
            
            if (Date.now() < end) {
                requestAnimationFrame(frame);
            }
        }());
    },
    
    canvasConfetti : function(component, event, helper){
        debugger;
        var canvas =  document.getElementById("customCanvas"); //component.find("customCanvas");
        
        
        // save this function... we'll save it to the canvas itself for
        // the purpose of this demo
        canvas.confetti = canvas.confetti || confetti.create(canvas, {
            resize: true
        });
        
        canvas.confetti({
            spread: 70,
            origin: {
                y: 1.2
            },
            colors : component.get("v.colors")
        });
    },
    
    busrtMode : function(component, event, helper){
        
        var end = Date.now() + (15 * 75);
        
        // go Buckeyes!
        var colors = ['#610B0B','#FFFF00','#FF00BF','#0040FF','#585858','#00FFBF','#FE642E','#FFBF00','#0101DF','#FF8000','#00FF00','#FF0040','#A901DB','#0B0B3B','#FF0000'];
        
        (function frame() {
            confetti({
                particleCount: 7,
                startVelocity: 25,
                angle: 335,
                spread: 10,
                origin: {
                    x: 0,
                    y: 0,
                },
                colors: colors
            }); 
            confetti({
                particleCount: 7,
                startVelocity: 25,
                angle: 205,
                spread: 10,
                origin: {
                    x: 1,
                    y: 0,
                },
                colors: colors
            });
            
            confetti({
                particleCount: 7,
                startVelocity: 35,
                angle: 140,
                spread: 30,
                origin: {
                    x: 1,
                    y: 1,
                },
                colors: colors
            });
            
            confetti({
                particleCount: 7,
                startVelocity: 35,
                angle: 40,
                spread: 30,
                origin: {
                    x: 0,
                    y: 1,
                },
                colors: colors
            });
            
            if (Date.now() < end) {
                requestAnimationFrame(frame);
            }
        }());
    }
})

showConfettiCMP.css

.THIS .buttonBox{
    position:relative;
    display: inline-block;
    width: 100%;
    max-width : 200px;
    height: 100px;
    padding: 1px !important;
    border: 1px solid #d8dde6; 
}

.THIS.canvasCss{
    background-color: black;
    width : 100%;
    height : 250px;
}

Demo

For you guys, I created a small component and bundled it as an unmanaged package which you can use in directly in your flows for the Confetti effect. To extend the usability, you can add other interfaces in the component and use it accordingly.

Package: https://login.salesforce.com/packaging/installPackage.apexp?p0=04t6F000004DGGL&isdtp=p1

How would it look in flows?

Also, I have something interesting for you to play with. Consider giving this a try too!
https://www.kirilv.com/canvas-confetti/

Thank you for being an awesome reader! If you haven’t already, please subscribe to this blog for receiving all the latest updates straight into your inbox.  🙂

16 thoughts on “Add Confetti effect to Lightning components and Flows

  1. Pingback: Confetti & SweetAlert in Lightning Web Component - M Hamza Siddiqui

  2. Pingback: Show Confetti using Lightning Aura Components – IntelliForce

  3. I just installed package and got following error: Action failed: c:FlowConfettiCMP$controller$fireConfetti [confetti is not defined]
    Can u help me? 🙂

    Like

  4. Receiving an error on this can you assist revising component after new update ’23 ?
    ltng:require$controller$init [$A.lockerService.isLockerNextenabledForComponent is not a function. (In ‘$A.lockerService.isLockerNextenabledForComponent(c)’;$A.lockerService.isLockerNextenabledForComponent is undefined)]

    Like

  5. Is it possible to configure this as a flow ‘action’ rather than an element within a screen? I would like the confetti to trigger after an opportunity is closed won without the need to present another screen. Thanks

    Like

    • Hi,

      It’s not possible with this version. However, it’s possible to have it the way you requested. However, it requires some additional effort and I only have very limited free time.
      If it interests you, we can have a paid arrangement and I can build this for you. 🙂

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.