Try it
Shows a worked example of the output, not a run against your account.
dataLayer
Nothing yet
GTM Custom HTML Tag (JavaScript)
<script>
(function() {
function fireConfetti() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'growth_milestone_unlocked',
timestamp: new Date().toISOString()
});
var canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:999999;pointer-events:none;';
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var colors = ['#16A34A', '#2563EB', '#F59E0B', '#EC4899', '#8B5CF6'];
var particles = [];
for (var i = 0; i < 100; i++) {
particles.push({
x: canvas.width / 2,
y: canvas.height / 2 - 50,
vx: (Math.random() - 0.5) * 16,
vy: (Math.random() - 0.8) * 18,
size: Math.random() * 8 + 5,
color: colors[i % colors.length],
gravity: 0.35,
tilt: Math.random() * 10
});
}
var frame = 0;
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var j = 0; j < particles.length; j++) {
var p = particles[j];
p.x += p.vx;
p.y += p.vy;
p.vy += p.gravity;
p.tilt += 0.1;
ctx.fillStyle = p.color;
ctx.fillRect(p.x, p.y, p.size, p.size * Math.cos(p.tilt));
}
frame++;
if (frame < 160) requestAnimationFrame(render);
else canvas.remove();
}
render();
var badge = document.createElement('div');
badge.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#16A34A;color:#fff;padding:14px 24px;border-radius:100px;box-shadow:0 10px 30px rgba(22,163,74,0.4);font-family:sans-serif;font-weight:600;font-size:14px;z-index:9999999;animation:fadeIn 0.3s ease;';
badge.innerText = "🎉 Milestone Unlocked: 15% Consulting Discount Applied!";
document.body.appendChild(badge);
setTimeout(function() { badge.remove(); }, 5000);
}
window.__goodlabs_confetti = fireConfetti;
})();
</script>How it works
- Dynamically mounts a full-viewport
canvaswithpointer-events: none. - Spawns 100 squares with randomised velocity, colour and the same downward gravity.
- Animates on
requestAnimationFramefor 160 frames, then removes the canvas. The discount badge stays 5 seconds. - Pushes
{ event: 'growth_milestone_unlocked', perk: '15_percent_discount' }.
Step-by-step installation
- Create a Custom HTML tag in GTM with the snippet above.
- Set trigger to your Lead Form Success / Thank You trigger.
- Test in GTM Preview Mode.
Frequently asked questions
Can I trigger this on button clicks?
Point the tag at a Click trigger instead of the form-success one. Nothing in the code depends on which trigger fires it.