I know I'm approx 1.5 months late with this one but here it is, I made a SnowFall Effect for Silverbullet because why not
It might me a fun mood setter for next holiday/winter season
![]()
space-lua
local snowflakesCount = 30
function toggleSnowfall()
local existing = js.window.document.getElementById("sb-snow-root")
if existing then
existing.remove()
clientStore.set("snow_enabled", "false")
return
end
-- Create the container
local container = js.window.document.createElement("div")
container.id = "sb-snow-root"
js.window.document.body.appendChild(container)
clientStore.set("snow_enabled", "true")
-- Inject the generator script
local scriptEl = js.window.document.createElement("script")
scriptEl.innerHTML = [[
(function() {
const root = document.getElementById("sb-snow-root");
const count = ]] .. snowflakesCount ..[[;
for (let i = 0; i < count; i++) {
const flake = document.createElement("div");
flake.className = "snowflake";
const size = Math.random() * 5 + 2 + "px";
const left = Math.random() * 100 + "vw";
const duration = Math.random() * 5 + 5 + "s";
const delay = Math.random() * 5 + "s";
const opacity = Math.random() * 0.6 + 0.2;
flake.style.width = size;
flake.style.height = size;
flake.style.left = left;
flake.style.animationDuration = duration;
flake.style.animationDelay = delay;
flake.style.opacity = opacity;
if (Math.random() > 0.8) {
flake.style.filter = "blur(3px)";
}
root.appendChild(flake);
}
})();
]]
container.appendChild(scriptEl)
end
-- Persistence Logic
function restoreSnowfall()
local isEnabled = clientStore.get("snow_enabled")
local existing = js.window.document.getElementById("sb-snow-root")
if isEnabled == "true" and not existing then
toggleSnowfall()
end
end
event.listen { name = "editor:pageLoaded", run = function() restoreSnowfall() end }
command.define {
name = "Snowfall: Toggle",
run = function() toggleSnowfall() end
}
space-style
#sb-snow-root {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
overflow: hidden;
}
.snowflake {
position: absolute;
top: -10px;
background: white;
border-radius: 50%;
opacity: 0.8;
filter: blur(1px);
animation: fall linear infinite;
}
@keyframes fall {
0% {
transform: translateY(0) translateX(0);
}
25% {
transform: translateY(25vh) translateX(15px);
}
50% {
transform: translateY(50vh) translateX(-15px);
}
75% {
transform: translateY(75vh) translateX(15px);
}
100% {
transform: translateY(100vh) translateX(0);
}
}