Shopify Snow Effect Generator
Seasonal snow, confetti or hearts, on a schedule, that turns off for anyone who prefers reduced motion.
Your details
Your snippet file (snippets/seasonal-effect.liquid)
{%- comment -%}
Seasonal snow effect, shown only between the dates below (checked in the visitor's own
browser clock) and skipped entirely for anyone with reduced motion turned on in their OS.
{%- endcomment -%}
<script>
(function () {
var start = new Date('2026-12-01T00:00:00');
var end = new Date('2027-01-02T23:59:59');
var now = new Date();
if (now < start || now > end) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:9999';
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var effect = 'snow';
var color = '#ffffff';
var count = 60;
var particles = [];
function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }
resize();
window.addEventListener('resize', resize);
function make() {
return {
x: Math.random() * canvas.width,
y: Math.random() * -canvas.height,
size: 4 + Math.random() * 6,
speed: 0.6 + Math.random() * 1.4,
drift: (Math.random() - 0.5) * 1.2,
spin: Math.random() * Math.PI * 2,
spinSpeed: (Math.random() - 0.5) * 0.1
};
}
for (var i = 0; i < count; i++) particles.push(make());
function heart(x, y, size) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.bezierCurveTo(x, y - size / 2, x - size, y - size / 2, x - size, y);
ctx.bezierCurveTo(x - size, y + size / 2, x, y + size, x, y + size * 1.4);
ctx.bezierCurveTo(x, y + size, x + size, y + size / 2, x + size, y);
ctx.bezierCurveTo(x + size, y - size / 2, x, y - size / 2, x, y);
ctx.fill();
}
function tick() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(function (p) {
p.y += p.speed;
p.x += p.drift;
p.spin += p.spinSpeed;
if (p.y > canvas.height + 20) { p.y = -20; p.x = Math.random() * canvas.width; }
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.spin);
if (effect === 'confetti') {
ctx.fillStyle = p.hue || (p.hue = ['#e11d48', '#f59e0b', '#10b981', '#3b82f6', '#a855f7'][Math.floor(Math.random() * 5)]);
ctx.fillRect(-p.size / 2, -p.size / 4, p.size, p.size / 2);
} else if (effect === 'hearts') {
ctx.fillStyle = p.hue || (p.hue = ['#e11d48', '#f472b6', '#fb7185'][Math.floor(Math.random() * 3)]);
heart(0, 0, p.size / 2);
} else {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
});
requestAnimationFrame(tick);
}
tick();
})();
</script>To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Snippets choose Add a new snippet, name it as shown above, paste this code and save. Then add {% render 'seasonal-effect' %} in layout/theme.liquid, just before </body>, so it runs on every page.
Dates are checked against each visitor's own device clock, not your store's timezone, so the cutoff can shift by a few hours depending on where a shopper is.
It's decorative and purely visual (a fixed, pointer-events:none canvas), so it never blocks clicks or taps on the page underneath.
Live preview
A canvas overlay, gated by date and by motion preference
A small <canvas> is added over the whole page (position:fixed, pointer-events:none, so it never blocks clicks or taps) and animates simple falling particles: circles for snow, colored rectangles for confetti, or small hearts. It only runs between the start and end dates you set, checked against the visitor's own device clock, not your store's timezone.
Anyone with prefers-reduced-motion turned on in their operating system never sees the effect at all: the script checks that preference before creating the canvas and simply does nothing if it's on, which is the accessible approach for a purely decorative animation.
Adding it to your theme
- 01
Add the snippet
Online Store → Themes → Edit code → Snippets → Add a new snippet named
seasonal-effect, paste the code, then add{% render 'seasonal-effect' %}inlayout/theme.liquid, just before</body>, so it runs on every page.
Questions people ask
Will it show for every visitor during the date range?
For every visitor except those with reduced motion turned on, since dates are checked against each device's own clock, the cutoff can shift by a few hours depending on the shopper's timezone.
Does it slow down the page?
It's a lightweight canvas animation with no external requests. Very high particle counts (over 150) on an older device can be noticeably heavier, so start around the default and raise it only if it still feels smooth.
Can I remove it automatically after the season?
Yes, that's the point of the end date: once it passes, the script does nothing at all, so you don't have to remember to delete the snippet.
Can I use my own particle color for confetti or hearts?
Confetti and hearts cycle through a small built-in set of festive colors for variety. The color field applies to snow, which looks best as a single consistent color.
Related tools
Last reviewed 30 September 2026.
